Nginx打印RequestBody

Nginx打印RequestBody

背景

新的日志收集构架中直接去掉 SpringBoot 服务,而是通过 Nginx 作为服务器,收集日志,打印成日志文件,通过 Flume 消费到 Kafka 中。由于日志收集的请求都是 Post JSON 的格式,所以需要在 Nginx 中获取 Post 的 Body。

问题

在配置 log_format 后,发现无法打印 $request_body

1
log_format user_log_format escape=json '{"time": "$msec", "ip": "$remote_addr", "ua": "$http_user_agent", "data": "$request_body"}';

查看官方文档,有如下注释。意思是说 $request_body 需要在带有 proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass 这些指令的 location 中,当 request_body 被读到内存缓冲区中使用。

1
The variable’s value is made available in locations processed by the proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass directives when the request body was read to a memory buffer.

Nginx 安装

由于在解决问题的时候需要使用到 ngx_lua 模块,所以我是直接安装的 OpenResty。
官网:http://openresty.org/cn/
安装文档:http://openresty.org/cn/installation.html

解决

在使用 (proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass) 指令的 location 中打印日志。

使用 HTTP Echo Module (OpenResty 中已经包括了这个模块)。

HTTP Echo Module 参考文档:https://www.nginx.com/resources/wiki/modules/echo/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
worker_processes  1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include /usr/local/openresty/nginx/conf/mime.types;
default_type application/octet-stream;

# 日志文件格式 access.log nginx默认的日志文件
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent $host "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 日志文件格式 user_defined.log 数据会写到这个文件中
log_format user_log_format escape=json '{"time": "$msec", "ip": "$remote_addr", "ua": "$http_user_agent", "data": "$request_body"}';

server {
listen 8080;
set $p_body "";
location / {
access_log /data/nginx/logs/user_defined.log user_log_format;
echo_read_request_body;
echo '';
}
}
}

使用 ngx_lua 模块

Lua 参考文档:https://www.nginx.com/resources/wiki/modules/lua/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
worker_processes  1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include /usr/local/openresty/nginx/conf/mime.types;
default_type application/octet-stream;

# 日志文件格式 access.log nginx默认的日志文件
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent $host "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 日志文件格式 user_defined.log 数据会写到这个文件中
log_format user_log_format escape=json '{"time": "$msec", "ip": "$remote_addr", "ua": "$http_user_agent", "data": "$request_body"}';

server {
listen 8080;
set $p_body "";
location / {
lua_need_request_body on;
content_by_lua 'local s = ngx.req.get_body_data()';

access_log /data/nginx/logs/user_defined.log user_log_format;
# echo ''; # 注意这个不能使用 echo 命令和 return 命令
}
}
}

还可以通过自定义的变量存放 request_body,然后再 log_format 中使用自定义变量打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
worker_processes  1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include /usr/local/openresty/nginx/conf/mime.types;
default_type application/octet-stream;

# 日志文件格式 access.log nginx默认的日志文件
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent $host "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 日志文件格式 user_defined.log 数据会写到这个文件中
log_format user_log_format escape=json '{"time": "$msec", "ip": "$remote_addr", "ua": "$http_user_agent", "data": "$p_body"}';

server {
listen 8080;
set $p_body "";
location / {
lua_need_request_body on;
content_by_lua '
local p_body = ngx.req.get_body_data() or ""
ngx.var.p_body = p_body
';
access_log /data/nginx/logs/user_defined.log user_log_format;
# echo ''; # 注意这个不能使用 echo 命令和 return 命令
}
}
}