Nginx 原生支持 JSON 日志输出,关键在于正确配置 log_format:需满足单行合法 JSON、字段名统一语义清晰、空值及特殊字符安全转义;推荐模板含时间戳、分层 IP、数值不加引号等容错设计,并配合 escape=json、缓冲日志及 Filebeat/Logstash 轻量解析。

想让 Nginx 的访问日志以标准 JSON 格式输出,直接被 Logstash 或 Filebeat 采集进 Elasticsearch?关键不在加插件,而在正确配置 log_format —— 它原生支持转义、变量拼接和结构化输出,配合合理的字段命名和空值处理,就能无缝对接 ELK。
JSON 日志格式必须满足的三个前提
ELK 链路对日志格式有隐性要求,仅“看着像 JSON”不够:
- 单行完整 JSON:每条日志必须是独立、合法、无换行的 JSON 对象(不能是 JSON Array,也不能跨行)
-
字段名统一且语义清晰:比如用
client_ip而非remote_addr,避免 Logstash 中反复重命名 - 空值/特殊字符安全转义:URL、User-Agent 中的引号、换行、控制字符必须被正确转义,否则 JSON 解析失败
推荐的 log_format JSON 模板(含容错处理)
以下配置已通过 Logstash json codec 和 Filebeat json.parse 验证,兼容主流 ELK 版本:
log_format json '{"@timestamp":"$time_iso8601",'
'"client_ip":"$http_x_forwarded_for",'
'"real_client_ip":"$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",'
'"upstream_addr":"$upstream_addr"}';
说明与建议:
-
时间戳用
$time_iso8601:ELK 默认识别该格式,无需 Logstash date filter 额外解析 -
IP 字段分层设计:区分代理透传的
$http_x_forwarded_for和真实连接的$remote_addr,便于溯源 -
数值字段不加引号:如
status、body_bytes_sent直接输出数字,ES 自动映射为long类型 -
字符串字段统一双引号包裹:Nginx 会自动转义内部引号,但需确保变量本身不含未转义换行(
$request_uri和$http_user_agent通常安全)
关键配置项补充(避免日志丢失或解析中断)
仅定义 log_format 不够,还需在 server 或 http 块中启用:
- 使用
access_log /var/log/nginx/access-json.log json buffer=64k flush=5s;:开启缓冲提升性能,5 秒强制刷盘防丢日志 - 添加
escape=json;(Nginx ≥ 1.11.8):显式声明 JSON 转义,更安全处理特殊字符 - 若后端服务可能返回空
upstream_response_time,改用"$upstream_response_time"(带引号)并设默认值:map $upstream_response_time $upstream_resp_time { "" "-"; },再在 log_format 中引用$upstream_resp_time
Logstash 或 Filebeat 端最小化配置示例
无需复杂 filter,轻量接入即可:
-
Filebeat(推荐):在
filebeat.inputs中设置json.keys_under_root: true和json.overwrite_keys: true,自动展开字段 -
Logstash:input 使用
file插件 + codecjson,无需额外filter { json { } },避免重复解析 - ES 索引模板中提前定义
@timestamp为date类型,其他数值字段设为long或float,防止动态映射出错










