必须用中间件+手动采集+脱敏控制记录api日志,因log::record()无上下文感知、易截断错位、无法获取请求/响应数据且不支持结构化分析。

ThinkPHP 记录 API 请求日志不能只靠 Log::record() 或开全局 LOG_RECORD,那样会混入大量无意义日志(如静态资源、健康检查),且无法结构化提取 input、response、duration。必须用中间件 + 手动采集 + 脱敏控制三者结合。
为什么不能直接用 Log::record() 记录 API 日志
因为 Log::record() 是内存缓存式写法,需配合 Log::save() 才真正落盘,而框架在请求结束前可能已触发多次 Log::save()(比如异常捕获、SQL 日志),导致你的 API 日志被截断或错位。更关键的是:它不感知请求上下文——你拿不到原始 $_REQUEST、响应体、耗时,也无法区分 GET /api/user 和 POST /api/user/edit。
-
Log::record()适合记录「某段逻辑内部状态」,不适合记录「完整 HTTP 事务」 - 全局开启
LOG_RECORD会把think\facade\Log::info()、error()全部塞进同一个文件,排查时得 grep 十分钟 - 默认日志格式不含 IP、模块名、请求路径,后期做聚合分析几乎不可行
如何用中间件精准拦截并记录 API 请求
核心是在请求进入控制器前采集输入,在响应返回前采集输出和耗时。推荐在 app/middleware.php 中注册一个高优先级中间件(如 App\Middleware\ApiLogMiddleware),并在 handle() 中完成闭环:
- 用
$request->isAjax()或路径前缀(如str_starts_with($request->pathinfo(), 'api/'))过滤非 API 请求 - 跳过
/api/ping、/public/、.js、.css等免日志路径,避免日志爆炸 - 记录前先获取客户端真实 IP:
$_SERVER['HTTP_X_FORWARDED_FOR']只取可信代理后第一个值(如 Nginx IP 段在10.0.0.0/8内才采信) - 用
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)提纯路径,去掉 query string 干扰 - 响应体从
$response->getContent()获取,但注意不要对大文件(如导出 Excel)做全文记录
怎么安全地记录入参和响应内容
直接 json_encode($_REQUEST) 风险极高:可能含密码、token、身份证号。必须脱敏后再记录:
- 定义白名单字段(如
['id', 'status', 'page', 'limit']),其余一律替换为'***' - 对敏感键名自动过滤:遇到
password、token、auth_key等,值强制设为空字符串或'[hidden]' - 响应体若为 JSON,先
json_decode($content, true),再递归清理敏感字段,最后再json_encode()回写 - 避免记录二进制响应(
$response->getHeader('Content-Type')含application/octet-stream就跳过)
耗时统计和日志通道分离的关键细节
耗时不能用 microtime(true) 简单相减——中间件嵌套、Swoole 协程切换会导致误差。正确做法是:
- 在中间件开头记一次
$start = $request->server('REQUEST_TIME_FLOAT')(该值由 Web Server 提供,稳定可靠) - 在
$next($request)返回后,用microtime(true) - $start算耗时,单位毫秒,保留一位小数 - 日志必须走独立通道(如
Log::channel('api')),否则会和系统错误日志混在一起;通道配置里要设'path' => LOG_PATH . 'api/',确保按天分文件 - 若用 Swoole 常驻模式,每次请求都必须重新
Log::channel('api'),不能复用上一次的 logger 实例
最易被忽略的是:日志通道切换必须在路由解析之后、控制器执行之前完成,否则 $request->module() 拿不到值,模块分流就失效了。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











