必须统一收集错误日志并携带trace_id和server_ip,通过monolog对接redis或elasticsearch,配合上下文中间件和php-fpm系统日志补全fatal错误。

错误日志不能只靠 runtime/log/ 目录
默认配置下,ThinkPHP 把日志写进 runtime/log/,每台服务器各写各的。集群里你根本不知道用户报错时连的是哪台机器,翻三台服务器的日志查一遍?线上出问题时没这时间。必须让所有节点把错误日志统一打到一个地方,且带可区分的上下文。
用 monolog + redis 或 elasticsearch 做集中收集
ThinkPHP 6.x 内置支持 Monolog,别自己 fopen/write。直接改 config/log.php,把 'default' => 'file' 换成 'default' => 'monolog',再配 driver:
- Redis 方案(轻量、低延迟):driver 设为
redis,host/port/database 必须和 Session 共用的 Redis 实例一致,避免多套中间件;key 名建议加前缀如log:tp6:error:,防止和其他业务混用 - Elasticsearch 方案(查得快、能聚合):需装
elasticsearch/elasticsearch包,配置里填hosts数组,注意 ES 的 index 名要带日期后缀(如tp6_error_20260917),不然单 index 过大会影响写入性能 - 别用 syslog 驱动:它依赖系统 daemon,容器化部署时容易丢日志,且无法携带 trace_id
必须带上 trace_id 和 server_ip 才算有效日志
光把日志发出去不够,得能定位到具体请求和机器。在 app/middleware.php 里加一个日志上下文中间件:
use think\facade\Log;
use think\middleware\TrustHost;
return [
// ...其他中间件
function ($request, $next) {
$traceId = $request->header('x-trace-id', uniqid('trc_', true));
Log::setContext(['trace_id' => $traceId, 'server_ip' => $_SERVER['SERVER_ADDR']]);
return $next($request);
}
];
-
$_SERVER['SERVER_ADDR']是当前 PHP 进程所在机器的真实内网 IP,比gethostname()更可靠 - 前端或 Nginx 要透传
X-Trace-ID,否则每次请求都是新 ID;Nginx 可用map指令生成并透传 - 不要在控制器里手动
Log::error()时再塞 trace_id —— 中间件已全局注入,重复写会覆盖
PHP Fatal Error 类错误不会进 Monolog,得靠 system log + php-fpm 配置
Monolog 只捕获异常和 trigger_error(),像 Fatal error: Allowed memory size exhausted 这种直接中断进程的错误,PHP 根本没机会调用日志驱动。这类错误只能从两处捞:
- php-fpm 的
slowlog和error_log:确保每台服务器的www.conf里开了catch_workers_output = yes,error_log = /var/log/php-fpm-error.log,然后用 filebeat 或 fluentd 收集这个文件 - systemd journal(如果用 systemd 启动 php-fpm):
journalctl -u php-fpm --since "2026-09-17 18:00:00"可查,但注意日志轮转策略,别被maxsize截断 - 别指望
register_shutdown_function()捕获所有 Fatal:它对内存耗尽、核心段错误等无效,纯属心理安慰
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











