需java应用主动计算并传递x-process-time响应头,apache通过%{x-process-time}o捕获记录,%d仅反映apache层耗时,无法体现java后端真实处理时间。
要让虚拟主机中通过 mod_log_config 记录真实的 java 后端处理时间(即 tomcat、spring boot 等应用内部耗时),不能只依赖 apache 自身的 %d 或 %t,因为它们只统计 apache 接收请求到发出响应头的时间,不包含 java 应用执行逻辑的时间。
真实后端耗时必须由 Java 应用主动计算并传递给 Apache,再由 mod_log_config 捕获。整个过程需两端配合:Java 层埋点 + Apache 层日志格式配置。
Java 应用端:添加 X-Process-Time 响应头
在请求进入和响应发出之间记录毫秒级耗时,并写入标准响应头:
- Spring Boot 可用
Filter实现:@Component public class TimingFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { long start = System.currentTimeMillis(); HttpServletResponse response = (HttpServletResponse) res; try { chain.doFilter(req, res); } finally { long duration = System.currentTimeMillis() - start; response.setHeader("X-Process-Time", String.valueOf(duration)); } } } - 确保该 Filter 生效(如注册为
@Bean或@WebFilter),且未被其他过滤器跳过。
⚠️ 注意:若使用异步 Servlet(如 AsyncContext),需额外处理线程上下文,否则 finally 中的时间可能不准。
Apache 虚拟主机配置:捕获并记录响应头
在对应 <virtualhost></virtualhost> 块内定义专属日志格式,引用 X-Process-Time 响应头:
<virtualhost>
ServerName api.example.com
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# 定义含后端耗时的日志格式(名称建议唯一,避免全局冲突)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{X-Process-Time}o" backend_timing
# 启用该格式记录访问日志
CustomLog logs/api_access.log backend_timing
</virtualhost>
-
%{X-Process-Time}o中的o表示 output header(响应头),必须与 Java 设置的 header 名完全一致(大小写敏感)。 - 若 Java 未设置该头,日志中对应位置显示
-,说明埋点未生效,需检查 Filter 是否覆盖全部路径、是否被异常中断等。
验证与排查要点
- 日志末尾出现类似
156284 128的两组数字:前者是%D(Apache 层耗时,微秒),后者是%{X-Process-Time}o(Java 层耗时,毫秒)。 - 若第二列恒为
-,检查:- Java 应用是否真的返回了
X-Process-Time头(可用curl -I http://...查看响应头); - Apache 是否启用了
mod_headers(非必需,但某些代理场景需确保头未被过滤); -
ProxyPassReverse是否正确,确保响应头透传(默认透传,但若中间有重写规则可能丢失)。
- Java 应用是否真的返回了
进阶建议
- 对比
%D和X-Process-Time:若后者远小于前者,说明网络或代理延迟高;若后者显著大于前者,可能是 Apache 配置异常(如mod_proxy缓冲问题)。 - 不建议混用
%T(秒级)记录后端时间,精度太低,无法反映真实性能波动。 - 如需区分不同服务(如用户服务 vs 订单服务),可让 Java 写入更细粒度头,如
X-Service-Time-User: 42,Apache 用%{X-Service-Time-User}o单独捕获。
不复杂但容易忽略。










