精简 log_format 的核心目标是减少日志事件中的字符串格式化操作——剔除高频高开销且非必需字段,如 %(asctime)s、%(funcname)s、%(lineno)d;改用轻量字段如 %(created)f、%(thread)d;压缩 %(name)s 路径;禁用运行时动态拼接;结构化日志需预序列化;极致场景用静态模板或 raw write。

精简 log_format 的核心目标不是“少打几个字”,而是减少每次日志事件中必须执行的字符串格式化操作数量——尤其要剔除那些高频、高开销、且对问题定位非必需的字段。
砍掉昂贵却低价值的时间与位置字段
像 %(asctime)s、%(funcName)s、%(lineno)d 这类字段,表面看只是加个时间或行号,实则每次调用都要触发函数调用、栈帧解析、格式化转换。在每秒数万条日志的场景下,它们会成为 CPU 热点。
-
时间戳:默认
%(asctime)s调用time.strftime(),开销显著。改用%(created)f(毫秒级浮点数)或预生成的%(relativeCreated)d(毫秒差),性能提升明显 -
方法名与行号:
%(funcName)s和%(lineno)d需遍历调用栈获取,延迟高。若已通过模块名%(name)s和 trace_id 定位上下文,可直接移除 -
线程名:
%(threadName)s在线程池复用场景下价值有限,且需反射获取;改用轻量%(thread)d(线程 ID 整数)更高效
压缩模块路径,避免重复解析包名
%(name)s 若不加限制,会输出完整包路径(如 com.example.service.order.OrderService),每次都要做字符串截断和缩写运算。这不是展示艺术,是算力消耗。
- 用
%(name)s的缩写语法,如 Logback 的%logger{20}或 Python logging 的%(name)-20s,强制截断并缓存结果 - 避免嵌套过深的 logger 命名(如
com.a.b.c.d.e.f.G),统一按业务域扁平命名(order、payment),从源头减少格式化负担
禁用动态字段,杜绝运行时拼接逻辑
有些自定义 formatter 会插入 %(ip)s、%(user_agent)s 等字段,背后是每次取 request 对象再调属性——这本质是隐式字符串拼接+对象访问,且无法被日志级别跳过。
- 这类字段应提前计算好,作为
extra字典传入(如logger.info("req", extra={'ip': ip, 'ua': ua})),由 formatter 统一输出,避免重复提取 - 绝对不要在
format()方法里做str(request.headers)或json.dumps(data)——这些操作在每条日志上都执行,无论是否输出 - 结构化日志(JSON)也需警惕:确保
record.__dict__中字段已为字符串,而非待序列化的 dict/list;否则json.dumps()就成了隐藏的 CPU 杀手
用静态模板替代运行时解析
通用 formatter 如 %(levelname)s - %(message)s 看似简单,但底层仍需正则匹配 + 字段查找 + 字符串替换。在极致优化场景,可考虑:
- 使用预编译的固定格式字符串(如
"{} {} {}\n".format(level, ts, msg)),绕过 logging 框架的通用解析器 - 在异步 logger 或高性能通道中,直接构造字节流(如
b"INFO 1729453260.123 user login\n"),彻底跳过字符串对象创建 - 对关键高频日志(如计数器、心跳),改用无格式的 raw write(如 Linux
write(2)系统调用),零格式化开销











