thinkphp默认不记录http状态码,因file日志驱动仅捕获log::record()主动写入的日志,而状态码在response发送前才确定;需通过response_send事件监听,用response::getcode()获取并记录。

ThinkPHP 日志为什么默认不记录 HTTP 状态码
因为 thinklogdriverFile 默认只捕获 Log::record() 主动写入的日志,而响应状态码(如 404、500)属于 HTTP 层输出结果,框架在 Response 发送前才确定,日志驱动本身并不感知。直接改日志配置或加 Log::record() 到控制器里,既分散又漏覆盖(比如中间件抛异常、路由未匹配等场景)。
- 状态码生成时机晚于常规日志写入点
-
app_debug = true时错误会走异常页面,500可能根本没进日志 - 用
hook或event监听response_send是唯一稳定入口
用 response_send 事件写入带状态码的日志
在 app/event.php 中注册监听,确保它在响应真正发送前触发。注意:必须用 Response 对象的 getCode() 获取最终状态码,不能依赖 http_response_code()(可能被多次调用干扰)。
return [
'response_send' => [
'app\listener\LogResponseStatus',
],
];
对应监听器 app/listener/LogResponseStatus.php 内容如下:
<?php namespace applistener;
use thinkacadeLog;
use thinkResponse;
class LogResponseStatus
{
public function handle(Response $response)
{
$code = $response->getCode();
if (in_array($code, [400, 401, 403, 404, 422, 500, 502, 503, 504])) {
$uri = request()->url(true);
$method = request()->method();
Log::channel('status')->info("{$code} {$method} {$uri}");
}
}
}
-
Log::channel('status')需提前在config/log.php中定义独立通道 - 避免在
handle()中调用$response->send()或修改响应体,否则会破坏输出流程 - 不要用
Log::error()代替Log::info()—— 状态码本身不是错误,分类归档才用级别
配置多通道日志按状态码自动分流
在 config/log.php 中新增 status 通道,并用 filter 限制只写入含特定状态码前缀的记录。关键点是:过滤函数接收的是原始日志数据数组,需检查 data 字段是否包含状态码文本。
'status' => [
'type' => 'File',
'level' => ['info'],
'path' => runtime_path() . 'log/status/',
'filter' => function ($data) {
return isset($data['data']) &&
is_string($data['data']) &&
preg_match('/^(4|5)d{2}s/', $data['data']);
},
],
-
filter函数返回false时该条日志会被丢弃,不写入文件 - 不能用
$data['level'] === 'info'做判断——因为所有状态码日志都走info,靠内容区分更准 - 目录
runtime/log/status/需手动创建并确保可写,否则静默失败
查日志时如何快速定位某类状态码
别打开整个文件翻。直接用命令行按前缀筛选(Linux/macOS):
grep '^404 ' runtime/log/status/*.log grep '^500 ' runtime/log/status/*.log
Windows 用户可用 PowerShell:
Select-String -Path "runtimelogstatus*.log" -Pattern "^502 "
- 注意空格:正则
^404后面有空格,匹配"404 GET /api/user"这种格式 - 日志文件名带日期,如
20240512.log,用通配符*.log覆盖多天 - 如果用了
daily驱动,得确认type是DailyFile,且filter仍生效——它对每条记录单独判断
状态码日志的真正难点不在写入,而在确保它不干扰主日志流、不重复记录、不因异常跳过。一旦监听点选错(比如用 app_end),就可能漏掉 header 已发送但 body 异常的情况。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











