apache自身耗时用%d(微秒),后端真实耗时需应用通过x-response-time响应头传递并用%{x-response-time}o捕获,二者共存可定位链路瓶颈;tcp连接与tls握手耗时无法通过mod_log_config获取。

Apache 的 mod_log_config 本身只能记录它自己处理请求的耗时(比如接收请求到发出响应头的时间),无法直接感知后端应用的执行时间,更无法测量“请求发给后端 → 后端处理 → 响应返回 Apache”这个完整往返过程(即网络往返时延,RTT)。要同时记录这两类时间,必须分层配合:Apache 记自身耗时,后端应用主动上报处理耗时,再由 Apache 日志捕获并共存。
记录 Apache 自身处理耗时:%D 是核心字段
%D 可记录 Apache 从收到请求第一个字节,到发送完响应头所经过的微秒数。这是最准确、开销最小的服务器层耗时指标。
- 在
httpd.conf或虚拟主机配置中定义日志格式:LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" apache_time - 启用该格式:
CustomLog logs/access_log apache_time - 日志示例末尾数字为微秒:
"GET /api/data HTTP/1.1" 200 312 "-" "curl/7.68.0" 42891→ 约 42.9 ms - 注意:
%T是秒级整数(截断小数),精度不足;%{msec}t是时间戳毫秒部分,不是耗时,别混淆
记录后端真实处理耗时:靠响应头传递
Apache 不知道 Tomcat 或 Spring Boot 里代码跑了多久。必须让后端应用在响应前计算并写入一个标准响应头,例如 X-Response-Time(推荐统一用前者)。
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
- Spring Boot 过滤器示例:
long start = System.currentTimeMillis();<br>chain.doFilter(request, response);<br>long duration = System.currentTimeMillis() - start;<br>response.setHeader("X-Response-Time", String.valueOf(duration)); - 确保头名一致且不被中间件吞掉(如 Nginx 前置需配
proxy_pass_request_headers on) - Apache 配置中用
%{X-Response-Time}o捕获(小写o表示 output headers):LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{X-Response-Time}o" java_rtt - 若日志中该位置显示
-或0,大概率是后端没设头、头名拼错、或被代理/压缩模块拦截
区分两类时间的实际意义
%D 和 %{X-Response-Time}o 数值通常不相等,差值能暴露链路瓶颈:
- 若
%D ≈ %{X-Response-Time}o:说明耗时主要在后端层,Apache 开销可忽略 - 若
%D ≫ %{X-Response-Time}o:可能是 Apache 层 SSL 解密、Gzip 压缩、Rewrite 规则复杂、或代理转发延迟高 - 若
%D :不合理,说明后端上报时间异常(如时钟漂移、头被篡改、或非同步上报)
补充:TCP 连接建立与 TLS 握手耗时不可通过 mod_log_config 获取
Apache 日志模块在 TCP 连接建立之后、HTTP 请求进入流程之前不工作,因此无法记录:
- TCP 连接建立耗时(含 DNS 查询):可用
%{time_connect}e—— 但这是由客户端(如 curl)提供,非 Apache 本地测量 - TLS 握手耗时:必须用
tcpdump + Wireshark抓包分析ClientHello到ServerHello时间差,或使用openssl s_client手动测试 - TCP 重传次数、RTT、SYN 超时等:需用
ss -i、tcpdump、bpftrace等系统工具,mod_logio仅统计应用层收发字节数,不接触协议栈










