Yii框架的请求日志怎么记录_使用beforeAction记录请求响应【心得】

5次阅读

beforeaction记录请求日志不可靠,因其触发时响应状态未定,无法获取真实statuscode、content等;应改用event_after_request事件,在响应结束后安全记录完整链路信息。

Yii框架的请求日志怎么记录_使用beforeAction记录请求响应【心得】

yii2 中用 beforeAction 记录请求日志为什么不可靠

因为 beforeAction 触发时,请求还没真正进入控制器逻辑,Yii::$app->response 的状态(比如 status code、headers、body)基本是空的或未定的;你这时去读 $response->statusCode$response->content,大概率拿到的是默认值(如 200、空字符串),不是最终返回结果。

常见错误现象:
– 日志里全是 200,哪怕接口实际返回了 404 或 500
– 响应体记录为空,或者只记到中间件/过滤器处理前的内容
并发下日志错位(一个请求的日志混入另一个请求的响应)

  • 真正需要的是「请求开始 + 响应结束」两个时间点的数据,beforeAction 只覆盖一半
  • 如果硬要在 beforeAction 里记日志,最多只能存 request info(method、url、IP、params),别碰 response
  • 想记完整链路,必须配合 afterAction 或更底层的事件(如 Application::EVENT_AFTER_REQUEST

推荐方案:监听 Application::EVENT_AFTER_REQUEST

这是 Yii2 官方预留的、最稳妥的响应后钩子。此时请求已完全结束,Yii::$app->response 已写入最终状态,包括 statusCodeheaderscontent(如果没被 stream 输出)都可安全读取。

使用场景:需要精确记录 http 状态码、响应耗时、原始响应体(调试用)、客户端真实 IP(经 trustedHosts 解析后)

  • config/web.phpcomponents 下给 application 加事件监听:
    'on afterRequest' => function ($event) {     $req = Yii::$app->request;     $res = Yii::$app->response;     $cost = Yii::$app->getLog()->getLogger()->getElapsedTime();     Yii::info([         'method' => $req->method,         'url' => $req->absoluteUrl,         'status' => $res->statusCode,         'cost_ms' => round($cost * 1000, 2),     ], 'request'); },
  • 注意 getElapsedTime() 返回的是从应用启动到当前的总耗时,不是单次请求耗时;更准的做法是手动打点(microtime(true) 放在 EVENT_BEFORE_REQUESTEVENT_AFTER_REQUEST 里)
  • 避免在该事件里调用 $res->send() 或修改 headers,此时响应已发出,再操作会触发 warning

记录响应体要注意的边界条件

直接读 $response->content 看似简单,但 Yii2 默认不缓存响应体——尤其用了 StreamResponseyiiwebResponse::sendFile() 或启用了 gzip/ob,content 就是空的。

性能影响明显:把整个响应体塞进日志,特别是大文件下载或图片接口,会吃光内存、拖慢响应、撑爆日志文件。

  • 仅在调试环境开启响应体记录,生产环境禁用:
    if (YII_DEBUG) {     Yii::info(['response' => substr($res->content, 0, 1024)], 'request'); }
  • substr($res->content, 0, 1024) 截断,防爆;别用 mb_substrcontent 是 raw bytes,不是 UTF-8 字符串
  • 如果用了 Response::setStream()content 永远为空,此时只能记「streamed: true」+ 文件路径或资源描述符

日志格式和字段设计建议

结构化日志比纯文本好查。Yii 默认用 FileTarget,字段全挤在一条 message 里,grep 起来费劲。用 json 格式输出,配合 elk 或 Loki 更省事。

容易踩的坑:
– 把敏感参数(如 password、Token)原样记进日志
– 用 print_r($req->bodyParams) 导致日志里出现大量换行和缩进,破坏 JSON 结构
– 忘记转义双引号,导致 JSON 解析失败

  • 过滤敏感字段再记录:
    $safeParams = array_diff_key($req->bodyParams, array_flip(['password', 'access_token']));
  • json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) 生成日志消息,别拼字符串
  • 固定字段建议包含:timestamp(用 date('c'))、ip$req->getUserIP())、user_id(如有登录态)、trace_id(如果集成 OpenTracing)

真正难的不是怎么记,是怎么在不拖慢请求、不泄露数据、不污染日志的前提下,让每条记录能对得上一次真实用户行为。字段少点没关系,关键时候能定位到那一毫秒。

text=ZqhQzanResources