准确掌握thinkphp api响应耗时需避开浏览器network和nginx $request_time,推荐五种方法:一、performancemiddleware中间件统一计时;二、监听think\responsesend事件;三、控制器基类钩子;四、prometheus指标化打点;五、error_log轻量文本日志。

如果您在ThinkPHP项目中需要准确掌握每个API接口的实际响应耗时,则不能依赖浏览器Network面板或Nginx日志中的$request_time字段,因其不包含PHP应用层逻辑执行时间。以下是多种可落地的响应时间记录方法:
一、在PerformanceMiddleware中间件中统一计时
该方法通过PSR-15中间件机制,在请求进入和响应发送前精确打点,覆盖全部执行路径(包括异常中断场景),且不侵入业务代码。
1、在app/middleware/PerformanceMiddleware.php中定义中间件类,实现handle方法。
2、在handle方法开头调用$start = microtime(true)记录起始时间。
3、调用$next($request)执行后续流程,并接收$response对象。
4、在返回$response前调用$end = microtime(true),计算$duration = $end - $start。
5、将$duration以毫秒为单位写入响应头:$response = $response->header('X-Response-Time', round($duration * 1000, 2) . 'ms')。
6、确保该中间件注册于全局中间件队列,且位于Auth、Cors等前置中间件之后、ResponseSend之前。
二、利用AppServiceProvider监听think\ResponseSend事件
该方式基于ThinkPHP 6+生命周期钩子,在框架响应即将发出的最终节点捕获耗时,能完整涵盖模板渲染、输出缓冲、Gzip压缩等环节,避免漏计。
1、打开app/Providers/AppServiceProvider.php文件,在boot()方法中添加事件监听。
2、使用Event::listen('think\ResponseSend', function ($event) { ... })注册回调。
3、在回调内从$_SERVER['REQUEST_TIME_FLOAT']获取请求开始时间。
4、调用microtime(true)获取当前时间作为结束时间。
5、计算差值得到总耗时,并写入Log::info()或结构化日志,字段包含uri、method、status_code、duration_ms、trace_id。
6、注意避免在回调中执行阻塞操作,防止影响响应时效。
三、在控制器基类中使用beforeAction与afterAction钩子
该方案适用于需对特定控制器或分组接口做精细化耗时追踪的场景,通过继承方式注入计时逻辑,便于按模块启用或关闭。
1、创建BaseApiController类,继承think\Controller。
2、在类中声明私有属性$actionStart = 0。
3、重写beforeAction()方法,在其中执行$this->actionStart = microtime(true)。
4、重写afterAction()方法,在其末尾执行$end = microtime(true),并计算$took = round(($end - $this->actionStart) * 1000, 2)。
5、调用Log::channel('api')->info(sprintf('API %s %s took %dms', $this->request->method(), $this->request->url(), $took))。
6、确保所有API控制器均继承BaseApiController,而非直接继承think\Controller。
四、通过Prometheus中间件实现指标化打点
该方法面向可观测性体系建设,将响应时间转化为直方图指标,支持P95/P99统计与基线偏离告警,适合中大型生产环境。
1、安装prometheus/client_php扩展:composer require prometheus/client_php。
2、创建PrometheusMiddleware中间件,在__construct()中初始化CollectorRegistry单例。
3、在handle()开头调用$histogram = $registry->getOrRegisterHistogram(...),预设buckets为[0.01, 0.05, 0.1, 0.25, 0.5, 1, 2]。
4、在$next($request)执行后立即调用$histogram->observe($duration),其中$duration单位为秒,需将microtime差值除以1000。
5、确保label值已清洗:route使用Route::getRule()而非Request::url(),动态参数替换为占位符如/user/{id},HTTP method转为大写。
6、暴露/metrics路由时禁用所有业务中间件,并设置响应头Content-Type: text/plain; charset=utf-8。
五、使用error_log直接写入文本日志文件
该方法最轻量、无依赖,适合快速验证或低资源环境,通过系统级日志函数记录关键字段,避免引入额外组件开销。
1、在全局中间件或AppServiceProvider boot()中定义日志路径,例如$logPath = runtime_path('log/api_timing.log')。
2、在请求入口处(如中间件handle开头)执行$start = $_SERVER['REQUEST_TIME_FLOAT']。
3、在响应即将返回前(如中间件handle末尾)执行$end = microtime(true),计算$ms = round(($end - $start) * 1000)。
4、构造日志行:$line = date('Y-m-d H:i:s') . "\t" . $request->method() . "\t" . $request->url() . "\t" . $ms . "ms\n"。
5、调用error_log($line, 3, $logPath),确保PHP进程对$logPath所在目录具有写权限。
6、对敏感参数(如token、password)在记录前执行unset($_REQUEST['token']),防止日志泄露。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











