ThinkPHP无法捕获真正的网关或PHP层超时,只能统计业务层慢请求:在中间件中用microtime(true)计时,超阈值后写入日志或数据库;Nginx 504、Swoole worker kill等真超时需依赖运维日志(如error.log、slowlog)分析。

接口超时怎么被 TP 捕获到
ThinkPHP 本身不主动记录请求耗时或标记“超时”,所谓“超时统计”其实是你主动在 App\middleware\CheckRequestTime 或控制器基类里埋点,结合 PHP 的 microtime(true) 和配置的阈值来判断。真正的超时(如 Nginx 的 fastcgi_read_timeout 或 Swoole 的 worker_timeout)发生时,TP 已无机会执行任何逻辑——进程直接被杀,日志都写不完。
所以能统计的只有「业务层感知到的慢请求」,不是网络或网关层的真超时。
- TP 的
think\App::run()启动后才开始计时,入口前耗时(如路由解析、中间件加载)不包含在内 - 务必在
App::init()之后、App::exec()之前启动计时,推荐放在全局中间件的handle()开头 - 别依赖
$_SERVER['REQUEST_TIME_FLOAT'],它可能被 CLI 或某些代理篡改,用microtime(true)更可靠
如何在中间件里统计并记录慢请求
新建中间件 app/middleware/RecordSlowRequest.php,核心是对比当前耗时与预设阈值(比如 2000ms),超过就写入日志或数据库。
<?php namespace app\middleware;
<p>use think\facade\Log;
use think\facade\Db;<p>class RecordSlowRequest
{
protected $startTime;</p><pre class="brush:php;toolbar:false;">public function handle($request, \Closure $next)
{
$this->startTime = microtime(true);
$response = $next($request);
$duration = (microtime(true) - $this->startTime) * 1000; // ms
$threshold = config('app.slow_request_threshold', 2000);
if ($duration > $threshold) {
$logData = [
'url' => $request->url(),
'method' => $request->method(),
'ip' => $request->ip(),
'duration' => round($duration, 2),
'code' => $response->getCode(),
'create_time' => date('Y-m-d H:i:s'),
];
// 写日志(推荐,轻量、异步友好)
Log::channel('slow')->info('', $logData);
// 或写数据库(注意别拖慢主流程,建议丢进队列或用 insert ignore)
// Db::name('slow_log')->insert($logData);
}
return $response;
}}
- 日志通道需提前在
config/log.php中定义'slow'渠道,单独配置文件路径和保留天数 - 避免在慢请求路径里同步写 DB,TP 默认 MySQL 连接没设
timeout,可能引发连锁超时 - 如果用了 Swoole,记得在
onWorkerStart里重置日志句柄,否则日志可能错乱或丢失
为什么 think\facade\Debug::getRangeTime() 不适合做超时判断
Debug::getRangeTime() 返回的是框架内部各阶段(如 route、dispatch、view)的耗时快照,但它依赖 Debug::remark() 的埋点位置,且只在调试模式(app_debug = true)下生效。生产环境关闭调试后,所有 remark 调用直接跳过,返回空数组或 0。
- 线上开启
app_debug会暴露敏感路径、SQL、变量,绝对禁止 - 即使强制开启,
getRangeTime()也不含中间件、响应发送、输出缓冲等环节,统计口径残缺 - 它本质是开发辅助工具,不是性能监控设施
真实超时场景下,还能拿到哪些信息
当真正发生网关或 PHP 层超时(如 Nginx 报 504 Gateway Timeout,Swoole worker 被 kill),TP 的 ExceptionHandler 根本不会触发。唯一能抓到线索的地方是:
- Nginx 的
error.log:搜索"upstream timed out"或"client timed out" - PHP-FPM 的
slowlog:开启request_slowlog_timeout = 2s,它会记录卡在哪个 PHP 文件哪一行 - Swoole 的
worker_error_log:配合worker_error_log配置项捕获 fatal error 或超时退出堆栈 - 数据库慢查询日志:确认是否因 SQL 卡住导致整体阻塞(TP 自身不记录这个)
这些都不是 TP 源码能控制的,得靠运维侧配合。想靠 TP “自动发现并统计真超时”,本质上是个伪需求。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











