nginx中將POST數據寫到日誌裏面的正確方式

起初覺得是個很簡單的問題,網上一大片「讓nginx日誌支持記錄POST請求」之類的文章,因而照作,nginx.conf配置爲:nginx

log_format main '$remote_addr\t$remote_user\t[$time_local]\t"$request"\t$status\t$bytes_sent\t'
                '"$http_referer"\t"$http_user_agent"\t"$http_cookie"\t"$request_body"';
access_log logs/access.log main;

curl -d試之,無效。心想nginx不至於這麼坑,必定是打開方式不對,去官網詳細參閱了一番$request_body的說明:http://wiki.nginx.org/NginxHttpCoreModule#.24request_bodycookie

$request_bodyapp

This variable(0.7.58+) contains the body of the request. The significance of this variable appears in locations with directives proxy_pass or fastcgi_pass.curl

被弄得一頭霧水:只有在用了proxy_pass或者fastcgi_pass標記的location{ }裏面變量$request_body纔會生效?函數

想來不會有這麼無厘頭的限制,我和nginx wiki之間必定是有一個壞掉了,決定去nginx代碼裏面探究一番。ui

 


 

先找到"request_body"字樣:this

$ grep -A 2 -F "\"request_body\"" -r ./*
./src/http/ngx_http_variables.c:    { ngx_string("request_body"), NULL,
./src/http/ngx_http_variables.c-      ngx_http_variable_request_body,
./src/http/ngx_http_variables.c-      0, 0, 0 },

想來ngx_http_variable_request_body()這貨就是用來讀取$request_body變量的回調函數了,下斷點gdb之:google

(gdb) b ngx_http_variable_request_body
Breakpoint 1 at 0x44cb90: file src/http/ngx_http_variables.c, line 1813.
(gdb) c
Continuing.
[Switching to Thread 182894133248 (LWP 25124)]

Breakpoint 1, ngx_http_variable_request_body (r=0x66a7c0, v=0x66b300, data=0) at src/http/ngx_http_variables.c:1813
1813    {
(gdb) l
1808    
1809    
1810    static ngx_int_t
1811    ngx_http_variable_request_body(ngx_http_request_t *r,
1812        ngx_http_variable_value_t *v, uintptr_t data)
1813    {
1814        u_char       *p;
1815        size_t        len;
1816        ngx_buf_t    *buf;
1817        ngx_chain_t  *cl;
(gdb) l
1818    
1819        if (r->request_body == NULL
1820            || r->request_body->bufs == NULL
1821            || r->request_body->temp_file)
1822        {
1823            v->not_found = 1;
1824    
1825            return NGX_OK;
1826        }
1827    
(gdb) p r->request_body
$1 = (ngx_http_request_body_t *) 0x0
(gdb) 

request_body竟然是0x0,難怪獲取不到。lua

再次google求助一番,發現一個麻煩的事情:nginx中讀取POST數據必需要調用ngx_http_read_client_request_body()函數,而默認狀況下,這個函數是不會被調用的。url

那麼nginx代碼中到底哪些地方會調用這個函數呢?

$ grep "ngx_http_read_client_request_body(" -r ./*
./src/http/ngx_http.h:ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
./src/http/modules/ngx_http_fastcgi_module.c:    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
./src/http/modules/ngx_http_uwsgi_module.c:    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
./src/http/modules/ngx_http_scgi_module.c:    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
./src/http/modules/ngx_http_proxy_module.c:    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
./src/http/modules/ngx_http_dav_module.c:        rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
./src/http/modules/ngx_http_proxy_module.c.orig:    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
./src/http/modules/perl/nginx.xs:    ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
./src/http/ngx_http_request_body.c: * on completion ngx_http_read_client_request_body() adds to
./src/http/ngx_http_request_body.c:ngx_http_read_client_request_body(ngx_http_request_t *r,

印證了前面wiki中的那一段話。

 


 

因而解決方法就很明顯了:要麼hack代碼強行調用一下,要麼找一個module能不那麼費事地幫忙調用一下。

天無絕人之路,這裏能夠用ngx_lua解決,只要在輸出log以前讀一遍request_body便可,照着例子作:http://wiki.nginx.org/HttpLuaModule#Synopsis

location /test {
    lua_need_request_body on;                                                                                            
    content_by_lua 'local s = ngx.var.request_body';
    ...
}

在location裏面加上兩行即解決問題,POST內容成功輸出到了日誌中,讚美Lua。

相關文章
相關標籤/搜索