Nginx结构化日志核心是输出合法单行JSON,需统一时间戳、转义特殊字符、精选语义明确字段,并配合Filebeat或Logstash直解析,避免grok开销及动态映射错误。

把 Nginx 日志写成结构化格式,核心是让每条日志变成机器可解析的固定模式,比如 JSON,这样后续用 ELK、Loki 或 Prometheus + Grafana 就能高效过滤、聚合和检索。关键不在“能不能”,而在于格式定义是否一致、字段是否语义明确、输出是否稳定。
用 log_format 输出 JSON 格式日志
Nginx 本身不原生支持 JSON,但可通过 log_format 拼接合法 JSON 字符串。注意引号、转义和空格处理,避免因非法 JSON 导致 Filebeat 或 Logstash 解析失败。
- 定义 JSON 格式(推荐使用
$time_iso8601替代$time_local,时间统一易解析):
log_format json_log '{"@timestamp":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request_method":"$request_method",'
'"request_uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time"}';
- 在 server 或 location 块中启用:
access_log /var/log/nginx/access-json.log json_log;
- 验证输出是否为单行合法 JSON(可用
jq -n .测试);确保所有变量值不含未转义双引号或换行,否则破坏 JSON 结构
关键字段选择与语义对齐
结构化不是字段越多越好,而是选业务和运维真正需要、能形成查询维度的字段。例如:
-
必选基础字段:时间戳(
@timestamp)、客户端 IP(remote_addr)、请求路径(request_uri)、状态码(status)、响应时长(request_time) -
增强可观测性字段:上游响应时间(
upstream_response_time)、转发来源(http_x_forwarded_for)、用户标识(http_x_user_id,需前端透传)、接口版本(http_x_api_version) -
规避空值陷阱:对可能为空的变量(如
$http_referer),可用"$http_referer"保留 null 字符串,或用 map 指令预设默认值
配合采集工具保持结构完整
即使日志是 JSON 格式,若采集层不做适配,也可能被当作文本切分丢失结构。常见做法:
-
Filebeat 配置示例:设置
json.keys_under_root: true和json.overwrite_keys: true,直接将日志行解析为事件字段 -
Logstash filter 推荐跳过:如果已输出标准 JSON,可省略 grok 过滤,直接用
json插件解析,降低 CPU 开销 -
ES 索引模板建议:提前定义
status为 integer、request_time为 float、@timestamp为 date 类型,避免动态映射出错
避免常见结构化陷阱
结构化日志容易因细节疏忽失效:
- 日志行末尾不能有多余空格或注释,JSON 必须严格单行
- 中文或特殊字符需确认 Nginx 编译时启用了 --with-http_ssl_module 和 UTF-8 支持,否则可能乱码或截断
- 高并发下
request_time和upstream_response_time是毫秒级浮点数,建议统一保留三位小数(Nginx 默认精度足够) - 不要在 log_format 中拼接复杂逻辑(如 if 判断),Nginx 不支持运行时条件表达式;如需条件字段,改用多个 access_log + if=condition 组合


















