PHP 错误异常日志分层的 Monolog 配置
来源:17golang原创
时间:2026-10-28 11:26:35 196浏览 收藏
PHP 项目把所有消息写进一个文件,排障时通常会遇到两个相反的问题:业务信息太多,真正的错误被淹没;异常信息又太少,值班人员无法判断影响范围。Monolog 的分层关键不在于“多建几个日志文件”,而在于先定义级别,再把 Handler 按目的绑定到不同输出。
官方地址:https://seldaek.github.io/monolog/
- 业务轨迹写入普通日志,异常和错误单独进入告警候选日志。
- Handler 的最低级别决定接收范围,
bubble决定记录是否继续向下传播。 - 上下文只保留排障所需字段,密码、令牌、Cookie 和原始请求体必须在进入 Logger 前剔除。
先把日志当成资产,再划分错误边界
建议先把记录分成三档:info 记录订单号、任务名和关键状态;warning 记录可恢复的降级、重试和外部依赖变慢;error 记录请求失败、未捕获异常和需要人工介入的结果。不要把每个调试细节都提升为 error,否则错误文件会失去告警价值。
从安全角度看,日志有三条攻击路径:用户输入可能制造伪造换行,异常上下文可能泄露凭据,日志文件权限过宽可能让同机进程读取隐私。因此“分层”同时要回答谁能读、保存多久、哪些字段不允许落盘。
用两个 StreamHandler 完成基础分层
下面的配置让业务日志接收 info 及以上记录,错误日志只接收 error 及以上记录。把错误 Handler 先压入栈,便于它先得到记录;两个 Handler 都保留传播,达到错误既进错误文件、又保留在业务日志中的效果。
# 安装 Monolog 3;生产环境使用 composer.lock 固定依赖版本 composer require monolog/monolog
pushHandler($appHandler);
$logger->pushHandler($errorHandler);
// 业务轨迹使用 info,字段只放可检索且不含秘密的标识
$logger->info('payment request accepted', ['order_id' => 'ORD-20261028-01']);
try {
// 这里替换成真实的支付调用;异常必须保留原异常对象便于追踪
throw new RuntimeException('payment provider timeout');
} catch (Throwable $exception) {
// 不把请求体、Authorization 或 Cookie 直接放入 context
$logger->error('payment request failed', [
'order_id' => 'ORD-20261028-01',
'exception' => $exception,
]);
}
这里的级别是“最低接收级别”:app.log 会接收 info、warning 和 error,error.log 只接收 error 及更严重记录。若把错误 Handler 的 bubble 改为 false,被它处理的记录将不再传给栈中更低位置的 Handler,除非你明确需要这种截断行为,否则不建议这样配置。

异常上下文要可排障,也要可审计
异常对象适合交给 Monolog 的 Formatter 统一展开,但请求参数要先做白名单或脱敏。最稳妥的做法是只传递请求 ID、业务 ID、依赖名称和耗时,不把整个 $_POST、请求头或响应原文塞进上下文。
$requestId ?? 'unknown',
'route' => $routeName ?? 'unknown',
'dependency' => 'payment-provider',
];
try {
$result = $client->charge($payload);
$logger->info('payment completed', $context + ['result' => 'accepted']);
} catch (Throwable $exception) {
// 用户可控的错误文本不要直接拼接到多行日志;保留异常类型与内部消息
$logger->error('payment failed', $context + [
'exception_class' => $exception::class,
'exception' => $exception,
]);
throw $exception;
}
如果日志要被采集到集中式平台,还应在 Formatter 或 Processor 层统一遮蔽字段,并限制日志目录的读取用户。过滤规则要覆盖 password、token、authorization、cookie 等不同大小写形式;不要只靠开发者记住“这次别打印”。

用 PHP 诊断配置和检查表收口
Monolog 负责应用层记录,PHP 自身的诊断日志仍受 error_reporting、log_errors 和 error_log 控制。生产环境应关闭页面直接显示错误,把诊断输出送到受控的错误入口;不要把 PHP 的底层错误和业务异常重复注册成两份。
| 检查项 | 正确结果 | 常见偏差 |
|---|---|---|
| 级别 | info 进业务日志,error 额外进错误日志 | 所有消息都用 error |
| 传播 | 按需保留 bubble,明确是否要截断 | 错误只进一个文件而无意丢失 |
| 字段 | 只记录 ID、路由、依赖和异常类型 | 直接记录请求体与 Authorization |
| 运维 | 目录权限、轮转、保留期都有负责人 | 日志无限增长或 Web 用户可下载 |
最后做一次故障演练:分别写入一条 info、warning 和 error,确认文件分布、格式和权限;再触发一次异常,确认没有重复告警,也没有敏感字段。检查通过后,再把错误日志接到值班系统,而不是直接把所有业务日志推给告警通道。
常见问题
为什么 error.log 里没有 warning?
因为它的最低级别设置为 Level::Error。需要收集 warning 时,应调整该 Handler 的级别或增加专门的 Handler,不要把所有调用点改成 error。
bubble 设为 false 会发生什么?
当前 Handler 处理记录后,记录不会继续传给后面的 Handler。它适合明确的“只发一次”或“到此为止”策略,普通分层通常保留为 true。
PHP 错误日志和 Monolog 错误日志要合并吗?
不必强行合并。先确定 PHP 诊断入口,再让应用异常由 Monolog 负责;关键是避免同一异常同时由全局处理器和业务 catch 块重复记录。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
137 收藏
-
423 收藏
-
347 收藏
-
355 收藏
-
356 收藏
-
280 收藏
-
497 收藏
-
351 收藏
-
420 收藏
-
239 收藏
-
385 收藏
-
273 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习