Administrator
Published on 2026-01-12 / 0 Visits
0

Nginx 日志 JSON 化输出

#AI

将 OpenResty 的日志(主要是访问日志,错误日志也可适配)输出为标准的 JSON 格式,方便后续日志解析、检索(如对接 ELK、Loki 等日志系统)。提供完整的配置方法,包含核心的日志格式定义、转义处理(避免 JSON 格式不合法),以及可直接复用的配置示例。

核心原理

OpenResty/Nginx 的 log_format 指令支持自定义日志格式,通过拼接 JSON 字符串、引用 Nginx/OpenResty 内置变量,并对特殊字符(如双引号、换行、斜杠)做转义,即可生成合法的 JSON 日志。

一、完整配置示例(访问日志 JSON 化)

1. 基础配置(nginx.conf)

在 OpenResty 的主配置文件(如 /opt/tmp/openresty-test/nginx/conf/nginx.conf)中,按以下结构修改:

# 全局块(可选,优化日志相关参数)
worker_processes  auto;
error_log  logs/error.log  warn;  # 错误日志暂保持默认,后面会讲JSON化方案
pid        logs/nginx.pid;

# HTTP 块:定义 JSON 格式的日志模板
http {
    include       mime.types;
    default_type  application/octet-stream;

    # ========== 核心:定义 JSON 格式的访问日志模板 ==========
    log_format json_log escape=json  # escape=json 自动转义双引号/换行等特殊字符
    '{'
        '"@timestamp":"$time_iso8601",'  # 日志时间(ISO8601格式,便于解析)
        '"remote_addr":"$remote_addr",'  # 客户端IP
        '"x_forwarded_for":"$http_x_forwarded_for",'  # 反向代理后的真实IP
        '"remote_user":"$remote_user",'  # 认证用户(无则为空)
        '"request_method":"$request_method",'  # 请求方法(GET/POST等)
        '"request_uri":"$request_uri",'  # 完整请求URL(含参数)
        '"request_length":"$request_length",'  # 请求体大小
        '"status":"$status",'  # 响应状态码
        '"body_bytes_sent":"$body_bytes_sent",'  # 响应体大小
        '"bytes_sent":"$bytes_sent",'  # 总发送字节数
        '"referer":"$http_referer",'  # 来源页
        '"user_agent":"$http_user_agent",'  # 客户端UA
        '"request_time":"$request_time",'  # 总请求耗时(秒,含小数)
        '"upstream_connect_time":"$upstream_connect_time",'  # 上游连接耗时
        '"upstream_response_time":"$upstream_response_time",'  # 上游响应耗时
        '"server_name":"$server_name",'  # 匹配的服务器名
        '"scheme":"$scheme",'  # http/https
        '"host":"$host",'  # 请求头的Host
        '"nginx_version":"$nginx_version"'  # Nginx/OpenResty版本
    '}';

    # 其他基础配置
    sendfile        on;
    keepalive_timeout  65;

    # ========== 应用 JSON 日志格式到具体服务 ==========
    server {
        listen       80;
        server_name  localhost;

        # 引用上面定义的 json_log 格式,指定日志输出路径
        access_log  logs/access_json.log  json_log;

        # 测试接口(可选,用于验证日志)
        location / {
            default_type text/plain;
            content_by_lua_block {
                ngx.say("Hello OpenResty!")
            }
        }
    }
}

2. 关键配置说明

配置项作用
log_format json_log escape=json定义名为 json_log 的日志格式,escape=json 是核心:自动转义 JSON 中的特殊字符(如 " 转成 \"、换行符转成 \n),避免 JSON 格式非法
$time_iso8601标准化时间格式(如 2026-01-12T10:00:00+08:00),比默认时间更易解析
各类 $xxx 变量OpenResty/Nginx 内置变量,对应请求的核心信息(完整变量列表可参考官方文档)

二、错误日志 JSON 化(进阶)

OpenResty 的错误日志默认是纯文本格式,无法直接通过 log_format 定义 JSON,但可通过 lua-nginx-module 实现自定义错误日志输出:

1. 配置示例(在 http 块中添加)

http {
    # 先定义错误日志的JSON输出函数(通过Lua)
    lua_shared_dict error_log_dict 10m;  # 共享内存(可选,用于缓存)
    init_by_lua_block {
        -- 自定义错误日志JSON格式化函数
        function log_error_json(level, msg)
            local error_json = {
                ["@timestamp"] = ngx.localtime(),
                ["level"] = level,
                ["message"] = msg,
                ["pid"] = ngx.worker.pid(),
                ["hostname"] = ngx.var.hostname or "unknown"
            }
            -- 将JSON写入错误日志文件(路径需和error_log一致)
            local f = io.open(ngx.config.prefix() .. "/logs/error_json.log", "a")
            if f then
                f:write(cjson.encode(error_json) .. "\n")
                f:close()
            end
        end
        -- 注册全局错误日志钩子
        ngx.log = function(level, ...)
            local msg = table.concat({...}, " ")
            log_error_json(ngx.errlog.level_by_num[level], msg)
            -- 保留原生错误日志输出(可选)
            require("ngx.errlog").log(level, msg)
        end
    }

    # 禁用原生错误日志(或保留,根据需求)
    error_log  logs/error_json.log  notice;
}

2. 注意事项

  • 需确保 OpenResty 启用了 lua-nginx-module(你当前编译的版本已包含);
  • 依赖 cjson 库(OpenResty 内置,无需额外安装);
  • 错误日志的 JSON 化属于进阶需求,若仅需解析访问日志,可忽略这一步。

三、验证配置是否生效

1. 检查配置语法

# 进入OpenResty的nginx目录
cd /opt/tmp/openresty-test/nginx/sbin

# 检查配置语法是否正确
./nginx -t

输出 nginx: configuration file /opt/tmp/openresty-test/nginx/conf/nginx.conf test is successful 即配置合法。

2. 重启 OpenResty

./nginx -s reload

3. 触发请求并查看日志

# 访问测试接口
curl http://localhost

# 查看JSON格式的访问日志
cat /opt/tmp/openresty-test/nginx/logs/access_json.log

预期输出(JSON 格式):

{
    "@timestamp":"2026-01-12T10:15:30+08:00",
    "remote_addr":"127.0.0.1",
    "x_forwarded_for":"",
    "remote_user":"",
    "request_method":"GET",
    "request_uri":"/",
    "request_length":"0",
    "status":"200",
    "body_bytes_sent":"16",
    "bytes_sent":"81",
    "referer":"",
    "user_agent":"curl/7.81.0",
    "request_time":"0.000",
    "upstream_connect_time":"-",
    "upstream_response_time":"-",
    "server_name":"localhost",
    "scheme":"http",
    "host":"localhost",
    "nginx_version":"1.27.1"
}

四、常见问题与优化

1. JSON 格式非法(如双引号未转义)

  • 必加 escape=json 参数(在 log_format 后),OpenResty 会自动转义 "\、换行符等;
  • 避免在日志中手动拼接双引号,优先使用内置变量。

2. 日志字段缺失(如 $upstream_response_time 为空)

  • 仅当请求转发到上游服务(如 proxy_pass)时,upstream_xxx 变量才有值,否则显示 -(正常);
  • 可通过 map 指令替换空值为 null(JSON 规范):
    http {
        map $upstream_response_time $upstream_time {
            "" "null";
            default $upstream_response_time;
        }
        # 然后在log_format中使用 $upstream_time 替代 $upstream_response_time
    }
    

3. 日志性能优化

  • 启用 access_log buffer=32k flush=5s:缓冲日志写入,减少磁盘 IO(适合高并发场景);
    access_log  logs/access_json.log  json_log  buffer=32k flush=5s;
    
  • 避免在日志中添加过多冗余字段(如不使用的 $request_completion)。

总结

  1. 核心步骤:在 http 块定义 log_format json_log(带 escape=json)→ 在 server 块引用 access_log xxx json_log → 重启验证;
  2. 关键保障escape=json 是避免 JSON 格式非法的核心,必须添加;
  3. 进阶优化:错误日志可通过 Lua 自定义 JSON 输出,访问日志可开启缓冲提升性能。

这套配置可直接用于生产环境,输出的 JSON 日志能被大部分日志分析工具(ELK、Grafana Loki、Fluentd 等)无缝解析。