比AccessLog更全面的原生Nginx 日志记录

一个原生 C 模块,让 Nginx 拥有完整 HTTP 审计能力------无需 Lua、无需 Sidecar、零运行时依赖。


网关层的 HTTP 审计,核心诉求很简单:记录完整的请求和响应对,包括 headers 和 body。

但原生 Nginx 的 access_log 天然做不到这一点------它只能记录 URI、状态码、响应时间等元数据,请求体和响应体不在其视野范围内。

OpenResty/Lua 方案可以补齐这个能力,但在金融、运营商、政企等对合规和稳定性要求极高的环境中,引入 LuaJIT 运行时本身就是一道门槛。


原生 Nginx 的审计盲区

先说说原生 Nginx 的 access_log 到底能记什么:

nginx 复制代码
log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent"';

能看到:谁、什么时候、访问了什么 URL、返回了什么状态码、花了多少字节。

看不到的:请求体里提交了哪些参数、后端返回了什么业务数据、内容在压缩前长什么样。

对于等保审计来说,这相当于只记了"某人进了门",但没记"他带走了什么文件"。

更麻烦的是,即使你自己写 C 模块,Nginx 的 body_filter 链是在 gzip 压缩之后执行的。这意味着你拿到的是压缩后的乱码,不是原始的业务数据。


OpenResty 不是银弹

有人可能会说:OpenResty + Lua 就能读 body 啊。

技术上确实可以:

lua 复制代码
-- access_by_lua 读请求体
ngx.req.read_body()
local req_body = ngx.req.get_body_data()

-- body_filter_by_lua 拼响应体(每个 chunk 都是新 Lua string)
resp_body = resp_body .. ngx.arg[1]

-- log_by_lua 输出 JSON 审计日志

但这个方案有几道隐形门槛:

1. 运行时依赖 = 合规风险

金融、运营商、政企的外网网关,安全基线通常写得明明白白:禁止在网关上引入动态语言运行时

LuaJIT 是动态执行的,代码可以热更新,这意味着审计逻辑本身可能被篡改。等保测评要的是"审计系统自身可被审计",Lua 层过不了这一关。

2. 拿不到原始 body

body_filter_by_lua 运行在 gzip 压缩之后 。想要原始 body,必须开 gunzip on,让 Nginx 先解压、Lua 审计、再压缩发出去。CPU 双倍开销不说,filter 顺序还可能冲突。

3. 性能是硬伤

我们在同硬件上做过对比(openEuler 22.03, i5-10400):

方案 RPS 相对原生
原生 Nginx 68,392 100%
OpenResty + Lua 审计 19,387 28%
nginx-flowlens (TLV) 32,422 47%

Lua 的字符串不可变性意味着每次 body_filter 拼接都要分配新内存。1MB 的响应体,几十个 chunk 下来,GC 压力直接爆炸。我们实测过,大文件场景下 Lua 方案的延迟能到秒级。

4. 维护成本

C 模块 crash 是段错误,coredump 一看就知道问题在哪。Lua 异常可能被 pcall 默默吞掉,线上出了问题你都不知道审计日志漏了没。


我们做了什么

nginx-flowlens 是一个 Nginx 原生 C 模块,只做一件事:在网关层完整捕获 HTTP 请求/响应对,输出结构化审计日志。

编译进 Nginx 二进制即可,两行配置启用:

nginx 复制代码
inspect on;
inspect_log /var/log/nginx/inspect.log;

核心设计:在正确的位置插桩

复制代码
NGX_HTTP_ACCESS_PHASE   → 捕获请求头 + 读请求体
top_header_filter       → 捕获响应头(gzip 之前)
top_body_filter         → 累积原始响应体(gzip 之前)
log handler             → 序列化为 TLV/JSON,写入日志

关键点在于 top_body_filter:我们在 gzip/brotli 压缩之前注册了 filter,所以拿到的是原始 body,不管客户端收到的是不是压缩后的。

为什么用 TLV 作为默认格式

审计日志是写密集场景。JSON 虽然人可读,但序列化开销太大:

格式 小请求 RPS 开销
baseline 32,441 ---
TLV 29,389 ~9%
JSON 2,811 ~91%

TLV 是紧凑二进制格式,序列化速度约为 JSON 的 10 倍。日常调试可以用 JSON,生产环境默认 TLV,需要分析时用工具转换:

bash 复制代码
python3 tools/tlv2json.py -i inspect.log -o audit.jsonl

子请求过滤

Nginx 的 auth_requestSSI 等内部机制会产生子请求。flowlens 通过 r != r->main 天然过滤,只审计用户原始请求,不会把内部鉴权请求混进审计日志。


适用场景

场景 为什么需要
等保/PCI-DSS 合规 三级等保明确要求"应能够记录应用系统的运行状态和用户行为,包括用户 ID、时间、事件类型、操作结果等"。没有 body 的日志等于半成品。
API 全链路追踪 当线上出现"某个请求返回了错误但抓包没抓到"时,审计日志里有完整的请求/响应对,可以直接 replay。
安全取证 疑似攻击请求进来时,安全团队需要看到完整的请求内容,包括 POST body 里的 payload。
数据变更审计 金融场景的转账、支付接口,需要留存完整的请求证据。

不是"比 OpenResty 快",而是"没有 OpenResty 也能做"

很多人听到"Nginx C 模块"的第一反应是:「为什么要自己写 C,OpenResty 不香吗?」

香不香取决于约束条件。如果你的环境允许装 LuaJIT、允许动态执行、对性能不敏感,OpenResty 确实够用。

但如果你在以下约束下工作:

  • 安全基线禁止网关引入 Lua VM

  • 等保要求审计系统自身可静态源码审查

  • 网关层性能预算有限(不能容忍 -70% RPS)

  • 需要捕获 gzip 前的原始 body

    那 OpenResty 本来就不是可选项。flowlens 的价值是从 0 到 1------让原生 Nginx 拥有原本只有外挂方案才能做到的完整审计能力。


快速体验

bash 复制代码
# 克隆项目
git clone https://github.com/kumustone/nginx-flowlens.git
cd nginx-flowlens

# 一键启动开发环境(无需 root)
./run-dev.sh install

# 发一条测试请求
curl -s -X POST http://localhost:19099/ -d '{"test":true}'

# 查看审计日志(TLV → JSON)
python3 tools/tlv2json.py -i .nginx-dev/logs/inspect.log

输出示例:

json 复制代码
{
  "timestamp": "2026-04-21T09:40:59.628Z",
  "client_ip": "127.0.0.1",
  "request": {
    "method": "POST", "uri": "/",
    "headers": {"Content-Type": "application/json"},
    "body": "eyJ0ZXN0Ijp0cnVlfQ=="
  },
  "response": {
    "status": 200,
    "headers": {"Content-Type": "text/html"},
    "body": "..."
  }
}

项目地址https://github.com/kumustone/nginx-flowlens

License:Apache 2.0

相关推荐
我星期八休息1 小时前
Linux系统编程—基础IO
linux·运维·服务器·c语言·c++·人工智能·算法
a752066282 小时前
零基础实操:小龙虾 AI OpenClaw 接入 Kimi 详细步骤
运维·服务器
Goldbioinformatics2 小时前
Windows版Claude Cowork启动Linux问题
linux·运维·windows
念恒123062 小时前
Ext系列文件系统(下)
linux·运维·服务器
布吉岛的石头3 小时前
Docker Compose编排实战:多容器应用从开发到生产
运维·docker·容器
身如柳絮随风扬3 小时前
Nginx 完全指南:核心用途、配置文件详解与动态配置实践
运维·nginx
2601_956139424 小时前
广州VI设计公司哪家强
linux·运维·服务器·python
@encryption4 小时前
RHCE --- 第三节
运维