起初覺得是個很簡單的問題,網上一大片「讓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。