Apache默认不支持%{HeaderName}o记录响应头,因该功能未实现;推荐用mod_lua通过r:note()写入notes再用%n记录,或由Java应用设置响应头后在LogFormat中用%{X-Request-ID}o捕获。

Apache 的 mod_log_config 默认不支持直接记录响应头(比如 X-Request-ID、X-Process-Time),因为 %{HeaderName}o 这类格式符在 Apache 2.4 及更早版本中未被实现——它只是语法上存在,实际运行时不会输出值,日志中显示为空或 -。
这不是配置错误,而是模块设计限制。要真正捕获自定义响应头内容,需绕过这个限制,用可行的替代路径。
使用 mod_lua 在响应后提取响应头(推荐)
适用于 Apache 2.4+ 且已启用 mod_lua 的环境。这是目前最稳定、侵入性最小的原生方案。
-
确保模块已加载:
LoadModule lua_module modules/mod_lua.so
-
在虚拟主机或全局配置中添加钩子:
LuaHookLog /path/to/log_response_headers.lua log_response
-
创建
log_response_headers.lua文件:function log_response(r) r:note("X_Request_ID", r.headers_out["X-Request-ID"] or "-") r:note("X_Process_Time", r.headers_out["X-Process-Time"] or "-") end -
在
LogFormat中引用 notes(用%{X_Request_ID}n):LogFormat "%h %t \"%r\" %>s %b %{X_Request_ID}n %{X_Process_Time}n" custom_with_notes CustomLog logs/access.log custom_with_notes
✅ 优点:不依赖 Java 应用修改,Apache 层闭环;✅ 支持任意响应头;⚠️ 注意:Lua 脚本需有读取权限,且不能抛出未捕获异常。
Apache Superset Dashboard and SQL Exploration Skill下载Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
让 Java 应用主动注入日志上下文(更常用、更可靠)
绝大多数生产环境选择这条路径:把响应头信息“同步”到 Apache 可读的日志字段中,避免在 Web 服务器层做复杂解析。
-
在 Spring Boot Filter 或 Servlet Filter 中设置 MDC 和响应头:
MDC.put("requestId", requestId); MDC.put("processTime", String.valueOf(System.currentTimeMillis() - start)); response.setHeader("X-Request-ID", requestId); response.setHeader("X-Process-Time", String.valueOf(System.currentTimeMillis() - start)); -
同时,用
CustomLog+%{X-Request-ID}o(注意是o)记录:LogFormat "%h %t \"%r\" %>s %b \"%{X-Request-ID}o\" \"%{X-Process-Time}o\"" java_context CustomLog logs/access.log java_context
✅ 优点:逻辑清晰、易调试、与链路追踪天然对齐;✅ 响应头由应用控制,100% 可信;⚠️ 前提:Java 应用必须确实设置了这些响应头,否则日志中为
-。
不推荐的方案:SetEnvIfNoCase 匹配响应头
网上有些资料建议用:
SetEnvIfNoCase ^X-Request-ID$ (.+) REQ_ID=$1
LogFormat "%{REQ_ID}e" ...但 SetEnvIfNoCase 只匹配请求头,对响应头无效。该写法在 Apache 中不会生效,属于常见误解。
补充说明:%D 和 %{X-Process-Time}o 的区别
-
%D是 Apache 自己测量的耗时(接收请求首字节 → 发送响应头完成),单位微秒,不含 Java 处理时间; -
%{X-Process-Time}o是 Java 应用自己计算并写入响应头的真实处理耗时,两者可并存用于对比瓶颈位置。
例如:
LogFormat "%h %t \"%r\" %>s %b %D %{X-Process-Time}o %{X-Request-ID}o" full_timing不复杂但容易忽略


















