Symfony结构化日志需配置Monolog的Formatter组件,首选JSON格式器;自定义Formatter可增减字段、调整时区;实时捕获日志数组需实现自定义Handler;注意formatter须置于handler下、避免全channel启用JSON。

Symfony 默认日志是纯文本格式,带时间戳、通道名、级别和消息,但不便于程序解析。要改成结构化格式(比如 JSON),关键不是“重写日志逻辑”,而是配置 Monolog 的 Formatter 组件——它负责把日志记录对象转成最终输出的字符串。
用 JSON 格式器直接输出结构化日志
这是最常用、最轻量的方式。每条日志会变成一行标准 JSON,后续可用 json_decode() 直接转为 PHP 数组。
- 在
config/packages/monolog.yaml中,为某个 handler(比如file或rotated)添加formatter: monolog.formatter.json - 确保该 handler 已启用,且其
channels包含你要记录的日志来源(如['main', 'payment']) - 示例片段:
file:
type: rotating_file
path: '%kernel.logs_dir%/app.log'
level: debug
formatter: monolog.formatter.json
channels: ['main']
自定义 Formatter 控制字段和时间格式
默认 JSON 格式器包含 message、context、level_name、channel、datetime 等字段,但不包含 trace、内存使用等扩展信息。如需增减字段或调整时间格式(如去掉微秒、改时区),需自定义 Formatter 类。
- 新建类
src/Log/CustomJsonFormatter.php,继承Monolog\Formatter\JsonFormatter - 重写
format()方法,在调用父类前修改$record['datetime']或注入额外键(如$record['memory'] = memory_get_usage()) - 在
services.yaml中注册为服务,并在 monolog 配置中引用它的 ID
不落盘,运行时直接拿到数组
如果目标不是写文件,而是想在代码里实时捕获日志数据为数组(例如做审计、发告警、存数据库),那就需要自定义 Handler,而不是 Formatter。
- 创建一个继承
Monolog\Handler\AbstractProcessingHandler的类,比如InMemoryArrayHandler - 在
write()方法中,把$record(已是关联数组)存入静态属性或服务容器中 - 在
services.yaml中声明为服务,并加标签monolog.handler - 在 monolog 配置中将它作为 handler 启用,注意设好
channels和level,避免干扰主流程
注意几个易错点
改格式后日志“看不见”或内容异常,往往不是 Formatter 问题,而是配置层级没对齐:
-
formatter必须写在具体 handler 下,不能放在monolog:顶层或handlers:列表外 - 开发环境若启用了
kernel.debug: false,debug级别日志会被全局拦截,即使 formatter 正确也收不到数据 - JSON 格式器默认用 UTC 时间,如需本地时区,得在自定义 Formatter 中调用
$record['datetime']->setTimezone(new \DateTimeZone('Asia/Shanghai')) - 结构化日志虽好,但不要对所有 channel 都开启 JSON——比如
consolehandler 用 JSON 就难以阅读,应单独配置formatter: monolog.formatter.line


















