monolog需手动配置handler、processor和formatter才能实现分类日志;默认streamhandler不分类,须用rotatingfilehandler按级别轮转、filterhandler过滤、processor注入请求id等上下文。

Monolog 本身不提供“强大”的日志能力,它只提供可组合的管道;真正强大的日志分类和行为,取决于你如何配置 Handler、Processor 和 Formatter。
为什么直接 composer require monolog/monolog 后日志还是乱成一团?
安装成功只是第一步。默认的 StreamHandler 只往一个文件写,所有级别(debug/info/warning/error)混在一起,没有按模块、请求 ID 或错误类型分流——这根本不算“分类”。
- 必须显式创建多个
Handler实例(比如RotatingFileHandler专写 error,FilterHandler套在StreamHandler外面筛出 debug 日志) - 单个
Logger实例不能自动分类;你需要为不同用途 new 不同的Logger,或用Logger::withName()+Handler的pushProcessor()注入上下文 - 忘记调用
$logger->pushHandler()是最常见漏操作——装了 Monolog 却没 handler,日志就彻底静音
怎样让 error 日志单独进 logs/error.log,且自动按天轮转?
靠 RotatingFileHandler,不是靠改文件名或写 cron 脚本。它内置基于日期的归档逻辑,但要注意构造参数顺序和权限。
- 第 1 个参数是完整路径:
new RotatingFileHandler('logs/error.log', ...)—— 路径不存在时不会自动创建目录,得提前mkdir -p logs - 第 2 个参数是最大日志文件数(如
7),不是保留天数;它按文件修改时间删旧档,不是按日期字符串匹配 - 必须显式设置日志级别:
new RotatingFileHandler('logs/error.log', 7, Logger::ERROR),否则默认是DEBUG,error 日志会被低级别 handler 再写一遍 - 如果 Web 服务器用户(如 www-data)没权限写
logs/,会静默失败,查error_log或开display_errors才能看到failed to open stream
怎么给每条日志自动带上当前请求 ID 和 URL?
用 Processor,不是在每处 $logger->info() 里手动拼数组。Monolog 的 Processor 是链式注入的,对性能影响极小。
- 写一个闭包处理器:
function ($record) { $record['extra']['request_id'] = $_SERVER['REQUEST_ID'] ?? uniqid('req-', true); return $record; } - 用
$logger->pushProcessor()注入,它会在所有 handler 执行前统一处理每条记录 - 别用
Logger::addRecord()手动传上下文——那只能作用于单次调用,无法全局生效 - 注意:PHP-FPM 环境下
$_SERVER可能不含REQUEST_ID,需在php-fpm.conf开启log_format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"并配合fastcgi_param REQUEST_ID $request_id;
真正的难点不在安装或写 handler,而在于 Handler 的层级关系和过滤优先级——比如 FilterHandler 包裹 StreamHandler 时,漏掉 setAcceptedLevels() 就会导致过滤失效;又比如多个 handler 共享 formatter 时,其中一个改了 setFormatter() 会影响全部。这些细节不跑一遍真实请求+日志输出,光看文档根本意识不到。











