Python logging 用 {} 占位时报 not all arguments converted
logging 的消息参数默认使用百分号合并;区分 Formatter 布局样式与消息占位符,修正格式异常并核验实际日志输出。
标准库 logging 输出变量时,下面的调用可能在日志处理阶段报错,原消息也没有正常写出:
logger.info('date={}', current_date)
TypeError: not all arguments converted during string formatting
这里把 {} 格式串和独立参数一起传给了 logging。默认日志消息合并使用 % 格式,所以应改为:
logger.info('date=%s', current_date)
本文针对 Python 3 标准库 logging 的默认消息处理,不适用于已经自行改写消息格式语义的第三方日志库或定制 LogRecord。
额外位置参数不是 print 的多个输出项
logger.info(message, *args) 把额外位置参数作为消息模板的替换值,不会像 print() 一样自动用空格连接。因此,下面的调用也有同类问题:
logger.info('finished', task_id)
消息里没有对应占位符,却提供了一个参数。应明确输出内容,例如:
logger.info('finished task=%s', task_id)
多个参数按模板顺序对应:
logger.info('task=%s state=%s', task_id, state)
核对占位符数量和类型;需要通用字符串表示时可用 %s,不要给非数值对象硬套 %d。没有额外参数时,logging 不会对消息执行这一步 % 合并,所以一条已经构造完成的字符串也可以正常记录。
Formatter 的 style 不改变消息参数规则
Python 3.2 起,Formatter 支持 style 参数。设置 Formatter(..., style='{') 后,logger.info('date={}', current_date) 仍然不是正确的默认消息调用。Formatter 的样式控制最终日志记录模板,比如 {levelname}、{message},不控制 info() 里消息与参数的合并。
下面的独立脚本同时使用花括号日志布局和百分号消息参数:
import logging
from datetime import date
logger = logging.getLogger('format_demo')
logger.setLevel(logging.INFO)
logger.propagate = False
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('{levelname}: {message}', style='{'))
logger.addHandler(handler)
logger.info('date=%s', date(2026, 1, 2))
logger.removeHandler(handler)
handler.close()
在默认 logging 实现下,这条消息应显示为 INFO: date=2026-01-02。这里的日期只是固定示例。片段供独立进程验证,不能在长期运行的应用里反复添加 handler;真实项目应保留框架已有的日志配置,只修出错的消息调用。
预先格式化也可以,但执行时机不同
以下方式先得到完整字符串,再交给 logging:
logger.info('date={}'.format(current_date))
Python 3.6 及以后也可使用 f-string:
logger.info(f'date={current_date}')
它们没有附加位置参数,不会再次拿 current_date 做 logging 的模板替换。不过,字符串构造会在调用日志方法之前发生,即使这条日志最终被级别过滤,也已经执行。
使用 logger.debug('value=%s', value) 可以延后消息格式化,但不能延后实参表达式本身的计算。例如 logger.debug('value=%s', expensive_call()) 仍会先执行 expensive_call()。确实需要避免昂贵计算时,可在调用前用 logger.isEnabledFor(logging.DEBUG) 判断;不要把“延迟格式化”理解成“所有参数都懒执行”。
也不要改成 logger.info(print(...))。这样内容先由 print 写到标准输出,传给 logger 的是 print 返回的 None,仍然没有得到所需日志消息。
核验实际日志内容
在开发环境让该条日志达到启用级别,检查它确实包含预期字段,且没有 Logging error 或相同格式异常。仅把级别调高、让错误日志不再触发,并不能修复调用。
如果消息格式已经修正,再次调用 logging.basicConfig() 后日志仍写到原来的位置,可继续检查 root logger 已有的 handler。消息占位符与输出配置需要分别检查。
不要为隐藏错误设置 logging.raiseExceptions = False,也不需要替换全局 Logger 类。错误有可能被 handler 的错误处理捕获并写到标准错误,而没有作为业务函数异常继续抛出,因此应检查日志输出本身。
上线前保留原调用与配置,避免把凭据或完整对象内容新增到日志中。需要回退时恢复本次消息模板修改;若另建了诊断 handler,只移除自己添加的对象,不要清空其他组件的日志输出。


