thinkphp需自定义中间件实现请求统计字段采集,推荐在app\middleware\requeststatsmiddleware中提取白名单参数、清洗非法值、json编码后异步写入带时间戳的日志表,避免敏感字段与日志爆炸。

ThinkPHP 本身没有内置的“请求统计自定义字段”功能,这不是框架的标准能力,而是需要你主动在中间件或控制器中采集、加工、落库的数据逻辑。直接套用 Request 对象或 Log::record() 是无效的——它们不自动记录字段级请求特征。
在全局中间件中捕获并记录自定义字段
最稳妥的位置是自定义中间件(如 App\Middleware\RequestStatsMiddleware),它能保证每次 HTTP 请求都经过,且可访问完整 Request 实例。注意不能用 __construct 初始化数据库连接,应延迟到 handle 中调用 Db::table() 或模型。
- 需手动提取字段:比如
$request->param('category')、$request->header('x-source')、$request->ip() - 避免记录敏感字段:如
password、token,建议白名单过滤:['uid', 'scene', 'from', 'v'] - 异步写入更安全:用
think-queue或简单go(function(){ Db::name('request_log')->insert($data); });(仅 Swoole 环境) - 表结构建议含:
id,url,method,params_json(TEXT),ip,create_time
用模型 + save() 写入带校验的统计记录
如果你已有 RequestLog 模型,且希望复用验证规则或事件钩子,save() 比原生 Db::insert() 更可控。但注意:模型默认会过滤非数据表字段,需在模型中显式声明 protected $field = ['url', 'params_json', 'ip', ...];,否则字段被静默丢弃。
- 构造数据时别直接传
$request->param()全量数组——可能含空值、文件资源、闭包等非法类型 - 推荐清洗方式:
array_filter($params, function($v) { return is_scalar($v) && strlen((string)$v) > 0; }) - JSON 字段存前务必
json_encode($cleaned_params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) - 性能敏感场景下,禁用模型事件:
(new RequestLog())->allowField(true)->save($data)
避免踩坑:日志爆炸与时间错乱
高频接口(如心跳、上报)若每请求都写 DB,不出三天表就过百万行,查询变慢,备份失败。同时,date('Y-m-d H:i:s') 在 CLI/Swoole 多进程下可能因时区或系统时间跳变导致时间戳异常。
- 加采样开关:例如只记录
app_debug == true或mt_rand(1, 100) === 1的请求 - 用
time()替代date()存整型时间戳,后续查范围更快:['create_time' => time()] - 禁止在
__destruct或register_shutdown_function中写统计——异常中断时可能连不上数据库 - 不要在
config/log.php里配level => ['sql', 'notice']来“替代”字段统计,它不记录业务参数
真正难的不是写几行 Db::insert(),而是想清楚哪些字段值得记、记多久、谁来查、查完怎么用。没设计好清理策略的统计表,半年后只会变成数据库里的“幽灵表”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











