从 $request_body 记录 POST 数据

Posted

技术标签:

【中文标题】从 $request_body 记录 POST 数据【英文标题】:Logging POST data from $request_body 【发布时间】:2011-06-23 18:54:16 【问题描述】:

我有我的配置设置来处理一堆 GET 请求,这些请求呈现的像素可以很好地处理分析和解析查询字符串以进行日志记录。使用额外的第三方数据流,我需要处理对给定 URL 的 POST 请求,该 URL 在其请求正文中具有预期的可记录格式的 JSON。我不想使用带有proxy_pass 的辅助服务器,只想将整个响应记录到关联的日志文件中,就像它对 GET 请求所做的那样。我正在使用的一些代码的 sn-p 如下所示:

GET 请求(效果很好):

location ^~ /rl.gif 
  set $rl_lcid $arg_lcid;
  if ($http_cookie ~* "lcid=(.*\S)")
  
    set $rl_lcid $cookie_lcid;
  
  empty_gif;
  log_format my_tracking ' "guid" : "$rl_lcid", "data" : "$arg__rlcdnsegs" ';
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
  rewrite ^(.*)$ http://my/url?id=$cookie_lcid? redirect;

这有点像我想要做的: POST 请求(不起作用):

location /bk 
  log_format bk_tracking $request_body;
  access_log  /mnt/logs/nginx/bk.access.log bk_tracking;

Curling curl http://myurl/bk -d name=example 给了我一个 404 页面未找到。

然后我尝试了:

location /bk.gif 
  empty_gif;
  log_format bk_tracking $request_body;
  access_log  /mnt/logs/nginx/bk.access.log bk_tracking;

冰壶curl http://myurl/bk.gif -d name=example 给了我一个405 Not Allowed

我当前的版本是nginx/0.7.62。非常感谢任何在正确方向上的帮助!谢谢!

更新 所以现在我的帖子是这样的:

location /bk 
  if ($request_method != POST) 
    return 405;
  
  proxy_pass $scheme://127.0.0.1:$server_port/dummy;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my.access.log my_tracking;

location /dummy  set $test 0; 

它正在正确记录发布数据,但在请求者端返回 404。如果我将上面的代码更改为像这样返回 200:

location /bk 
  if ($request_method != POST) 
    return 405;
  
  proxy_pass $scheme://127.0.0.1:$server_port/dummy;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
  return 200;

location /dummy  set $test 0; 

然后它正确返回200,但不再记录post数据。

另一个更新 Kinda找到了一个可行的解决方案。希望这可以帮助其他人。

【问题讨论】:

你在这方面有什么收获吗?我遇到了类似的问题。 不要尝试从虚拟位置执行set $test 0;,而是尝试从虚拟位置执行return 200;,然后它会按预期返回200,并且我仍然会记录身体。 【参考方案1】:

这个解决方案就像一个魅力(在 2017 年更新,以纪念 log_format 需要在 nginx 配置的 http 部分):

log_format postdata $request_body;

server 
    # (...)

    location = /post.php 
       access_log  /var/log/nginx/postdata.log  postdata;
       fastcgi_pass php_cgi;
    

我认为诀窍是让 nginx 相信你会调用一个 cgi 脚本。

【讨论】:

我必须移动“log_format postdata $request_body;”到服务器部分,因为 nginx 说“这里不允许使用 log_format 指令” 这是正确的答案。除了像@OlegNeumyvakin 提到的那样,我不得不移动log_format 部分。但在我的情况下,我必须将它移动到“http”块内,而不是“server”块内,它才能工作。但这成功了! @OlegNeumyvakin & Matt :我已将您的评论放入答案本身(目前正在等待审核)。谢谢! 我什至不得不将log_format 行移到服务器部分上方。但后来效果很好。 ( nginx/1.9.3 ) 是的,log_format 只允许在 nginx 配置的 http 部分。我相应地更改了示例。【参考方案2】:

试试 echo_read_request_body。

"echo_read_request_body ... 显式读取请求正文,以便 $request_body 变量始终具有非空值(除非正文太大以至于 Nginx 已将其保存到本地临时文件中)。"

location /log 
  log_format postdata $request_body;
  access_log /mnt/logs/nginx/my_tracking.access.log postdata;
  echo_read_request_body;

【讨论】:

这比接受的答案更好,因为它不需要安装 fastcgi apt-get install nginx-full安装 @user4我只能在我的日志中看到破折号。有什么问题?我使用 echo_read_request_body。并安装了 echo 模块 对我来说,如果我重定向(例如 301/302),request_body 就会消失 在位置指令中。我毫不犹豫地宣布,我对 nginx 的体验相对较低,但我并不清楚为什么 request_body 会消失,或者为什么会这样。【参考方案3】:

好的。所以最后我能够记录帖子数据并返回 200。这是一种我不太自豪的 hacky 解决方案,它基本上覆盖了 error_page 的自然行为,但是我对 nginx 和时间线的缺乏经验使我找到了这个解决方案:

location /bk 
  if ($request_method != POST) 
    return 405;
  
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_redirect off;
  proxy_pass $scheme://127.0.0.1:$server_port/success;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking;

location /success 
  return 200;

error_page   500 502 503 504  /50x.html;
location = /50x.html 
  root   /var/www/nginx-default;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my_tracking.access.log my_tracking_2;

现在根据该配置,代理通行证似乎总是会返回 200。有时我会得到 500,但是当我输入 error_log 以查看发生了什么时,我所有的 request_body 数据都在那里,我看不到问题。所以我抓住了这一点并写到了同一个日志中。由于 nginx 不喜欢跟踪变量的同名,我只使用了 my_tracking_2 并写入了与返回 200 时相同的日志。绝对不是最优雅的解决方案,我欢迎任何更好的解决方案。我看过 post 模块,但在我的场景中,我无法从源代码重新编译。

【讨论】:

您提出的解决方案与此处接受的答案非常相似:***.com/questions/17609472/… 由于 nginx 不会解析主体,除非发送到代理或快速 cgi 服务器,因此代理到自身可以工作. 我们使用类似的解决方案,通过 proxy_pass POST 请求到另一个位置。【参考方案4】:

下面的解决方案是我找到的最好的格式。

log_format postdata escape=json '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$request_body"';
server 
        listen 80;

        server_name api.some.com;

        location / 
         access_log  /var/log/nginx/postdata.log  postdata;
         proxy_pass      http://127.0.0.1:8080;
        


对于这个输入

curl -d '"key1":"value1", "key2":"value2"' -H "Content-Type: application/json" -X POST http://api.deprod.com/postEndpoint

产生出色的结果

201.23.89.149 -  [22/Aug/2019:15:58:40 +0000] "POST /postEndpoint HTTP/1.1" 200 265 "" "curl/7.64.0" "\"key1\":\"value1\", \"key2\":\"value2\""

【讨论】:

另外,请注意,使用 escape=none 只会输出 JSON,而不会转义引号(如果您的用例需要这样做) 不工作。有没有什么特别需要做的。例如需要添加一些特定的模块吗? 我在全新的 nginx 中进行了测试,如果您想测试,只需从 hub.docker.com/_/nginx 创建一个原始 docker 映像并尝试。也许你的 nginx 有不同的配置 此日志也包含 GET 请求。我正在尝试仅记录帖子 postdata 日志对我来说仍然是空的。【参考方案5】:

FWIW,这个配置对我有用:

location = /logpush.html 
  if ($request_method = POST) 
    access_log /var/log/nginx/push.log push_requests;
    proxy_pass $scheme://127.0.0.1/logsink;
    break;
     
  return 200 $scheme://$host/serviceup.html;
   
#
location /logsink 
  return 200;

【讨论】:

【参考方案6】:

nginx 日志格式取自这里:http://nginx.org/en/docs/http/ngx_http_log_module.html

无需额外安装任何东西

GETPOST 请求工作:

upstream my_upstream 
   server upstream_ip:upstream_port;


location / 
    log_format postdata '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$request_body"';
    access_log /path/to/nginx_access.log postdata;
    proxy_set_header Host $http_host;
    proxy_pass http://my_upstream;
    

只需更改 upstream_ipupstream_port

【讨论】:

【参考方案7】:

我遇到了类似的问题。 GET 请求有效,并且它们的(空)请求主体被写入日志文件。 POST 请求失败,出现 404。尝试了一下,我发现 所有 POST 请求都失败了。我找到了forum posting asking about POST requests,那里的解决方案对我有用。那个解决方案?在proxy_pass 行之前添加proxy_header 行,与以下示例中的完全相同。

server 
    listen       192.168.0.1:45080;
    server_name  foo.example.org;

    access_log  /path/to/log/nginx/post_bodies.log post_bodies;
    location / 
      ### add the following proxy_header line to get POSTs to work
      proxy_set_header Host $http_host;
      proxy_pass   http://10.1.2.3;
    

(这是 nginx 1.2.1 的价值所在。)

【讨论】:

【参考方案8】:

我认为正确答案如下图:

location /bk 
  if ($request_method != POST) 
    return 405;
  
  proxy_pass $scheme://127.0.0.1:$server_port/dummy;
  log_format my_tracking $request_body;
  access_log  /mnt/logs/nginx/my.access.log my_tracking;
  

location /dummy 
   access_log off; 
   return 200;

【讨论】:

以上是关于从 $request_body 记录 POST 数据的主要内容,如果未能解决你的问题,请参考以下文章

nginx日志中$request_body 十六进制字符(\x22) 引号问题处理记录

Nginx日志中request_body为空

Nginx记录post body内容

nginx 日志打印post请求参数

[debug]记一次竞态更新bug的解决

nginx从包含特定值的access_log中删除POST