python日志性能关键在正确用法:用占位符格式化、显式判断级别、避免耗时操作;选合适handler并缓冲;压测验证真实开销;结构化日志需延迟序列化或用structlog。

Python日志本身开销不大,但不当使用(如高频打印、未关闭调试日志、字符串拼接写法)会显著拖慢程序,尤其在高并发或循环中。关键不是“用不用日志”,而是“怎么用才不影响性能”。
日志级别和条件判断要配合好
即使日志级别设为 WARNING,如果写成 logger.debug("value=" + str(x) + ", time=" + str(time.time())),字符串拼接和函数调用仍会执行,造成无谓开销。正确做法是让格式化延迟到真正需要输出时:
- 用占位符写法:
logger.debug("value=%s, time=%s", x, time.time())—— 参数只在日志被启用且匹配级别时才求值 - 加显式判断(适合复杂逻辑):
if logger.isEnabledFor(logging.DEBUG): logger.debug(...) - 避免在日志语句里调用耗时函数(如
get_stack_info()、数据库查询等)
Handler选择和配置直接影响吞吐量
默认的 StreamHandler(输出到终端)在高频率下会成为瓶颈;FileHandler 在无缓冲/同步写入时也较慢。可优化方向:
- 生产环境禁用
StreamHandler,只保留RotatingFileHandler或TimedRotatingFileHandler - 开启行缓冲或块缓冲:
FileHandler(..., buffering=8192) - 考虑异步方案:用
QueueHandler + QueueListener把 I/O 移出主线程 - 避免在每个请求/循环中重复创建 Handler(应复用 Logger 和 Handler 实例)
用真实场景做开销压测,别只看单条耗时
单次 logger.info("ok") 可能只花 0.01ms,但 1 万次/秒持续打日志,I/O 累积、锁竞争、GC 压力就会暴露。建议这样评估:
立即学习“Python免费学习笔记(深入)”;
- 用
timeit测单条语句(对比不同写法):timeit.timeit(lambda: logger.debug("a=%s", 42), number=100000) - 在服务中注入日志压力测试:模拟峰值流量下开启 DEBUG 日志,观察 CPU、IO wait、响应延迟变化
- 用
logging.config.dictConfig动态开关日志级别,无需重启即可验证影响 - 结合
py-spy或cProfile查看日志相关函数是否进入热点(如Formatter.format、FileHandler.emit)
结构化日志和 JSON 输出需谨慎
用 json.dumps 打结构化日志很常见,但序列化本身有开销,尤其含嵌套对象或大字典时:
- 避免直接传
logger.info(json.dumps(data))—— 序列化总被执行 - 改用支持结构化的库(如
structlog),它默认延迟序列化,并提供绑定上下文能力 - 若必须用标准库,至少封装一层:
if logger.isEnabledFor(logging.INFO): logger.info(json.dumps(data)) - 对高频字段(如 request_id、user_id),优先用 LoggerAdapter 绑定,而非每次手动塞进 message











