1. 問題描述
nginx 在獲取post數(shù)據(jù)時候重挑,如果是中文,則轉(zhuǎn)換成16進制顯示在日志文件中,如下圖所示瓜喇。
日志格式為: log_format postdata '$remote_addr | $request_body | $resp_body';
此篇文章記錄下解決此次問題的過程屿脐。
最新版本解決方式
適合nginx 1.11.8
以上版本
在nginx 1.11.8
以上版本中log_format
增加了escape=json
參數(shù)涕蚤,在配置日志格式時加上此參數(shù)可以不轉(zhuǎn)義變量內(nèi)容,官方文檔-參數(shù)說明
日志配置
log_format postdata '$remote_addr | $request_body | $resp_body';
log_format postdata escape=json '$remote_addr | $request_body | $resp_body';
日志輸出
第一條日志是不加escape=json
參數(shù)后的诵,log_format
輸出的
第二條日志是加上escape=json
參數(shù)后万栅,log_format
輸出的
2. 軟件版本
- 系統(tǒng)
centos 6.7 X86_64
- nginx
1.11.5
- lua-nginx-module
0.10.7
- PHP
5.6.27
測試環(huán)境部署見:Nginx 使用lua-nginx-module 來獲取post請求中得request和response信息
3. 收集信息
收集信息-階段1:
在遇到此類問題的時候,我們大多是使用搜索引擎搜索答案西疤,因為這樣來的更快一些烦粒。當遇到這個問題的時候,我感覺也無從下手代赁,隨即在google中搜索答案扰她,沒過多久,便找到了同類人芭碍,也遇到了這個問題
此次搜索關(guān)鍵字: nginx log 中文 16進制
出處:https://groups.google.com/forum/#!topic/openresty/PYvvfj5RKCg
這個里面提到了:
為什么會出現(xiàn)這個問題义黎?
解決辦法
當時情況,在大量的搜索結(jié)果下豁跑,剛開始沒注意到這里面的問題廉涕,認為這個是openresty的解決辦法。就繼續(xù)搜索信息了艇拍。
收集信息-階段2:
經(jīng)過上面得信息狐蜕,我們可以得知,nginx現(xiàn)在是把中文字符轉(zhuǎn)換成16進制卸夕。
所以關(guān)鍵字變成了:nginx 不支持中文
從這個關(guān)鍵字便發(fā)現(xiàn)了下面得信息
來自: http://navyaijm.blog.51cto.com/4647068/1082169
從這里面獲得了:
- 通過降級nginx來解決問題
這位博主通過過降級nginx 程序來達到支持中文得效果层释,當時目測這文章是2012年得,比較久遠快集,而且還需要降級贡羔,就沒有嘗試這類方法。
信息收集-階段3:
這次搜索解決答案也有一段時間了个初,突然想起了階段1時發(fā)現(xiàn)得解決方法乖寒,里面有個命令可以關(guān)閉nginx轉(zhuǎn)換16進制得命令。隨即搜索關(guān)鍵字改成:
nginx log escape characters
通過這個關(guān)鍵字找到了下列有用信息院溺。
來自: http://mailman.nginx.org/pipermail/nginx/2008-January/003051.html
從這里面獲得了:
- 在2008年得時候楣嘁,通過這個path,讓不可打印得字符轉(zhuǎn)成16進制。
- attachment.bin 文件記錄了是哪個源代碼文件的補丁逐虚。
通過查看這個文件聋溜,發(fā)現(xiàn)了 ngx_http_log_escape
這函數(shù)是轉(zhuǎn)換16進制的。要知道nginx源代碼已經(jīng)被很多國人都閱讀過叭爱,肯定有相關(guān)的解釋撮躁。
隨即關(guān)鍵字變成了: nginx ngx_http_log_escape
通過搜索發(fā)現(xiàn)了下列的源碼解釋
static uintptr_t
ngx_http_log_escape(u_char *dst, u_char *src, size_t size)
{
ngx_uint_t n;
/* 這是十六進制字符表 */
static u_char hex[] = "0123456789ABCDEF";
/* 這是ASCII碼表,每一位表示一個符號买雾,其中值為1表示此符號需要轉(zhuǎn)換馒胆,值為0表示不需要轉(zhuǎn)換 */
static uint32_t escape[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
if (dst == NULL) {
/* find the number of the characters to be escaped */
n = 0;
while (size) {
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
n++;
}
src++;
size--;
}
return (uintptr_t) n;
/* 返回需要轉(zhuǎn)換的字符總數(shù)*/
}
while (size) {
/* escape[*src >> 5],escape每一行保存了32個符號,
所以右移5位凝果,即除以32就找到src對應的字符保存在escape的行,
(1 << (*src & 0x1f))此符號在escape一行中的位置睦尽,
相&結(jié)果就是判斷src符號位是否為1器净,需不需要轉(zhuǎn)換 */
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
*dst++ = '\\';
*dst++ = 'x';
/* 一個字符占一個字節(jié)8位,每4位轉(zhuǎn)成一個16進制表示 */
/* 高4位轉(zhuǎn)換成16進制 */
*dst++ = hex[*src >> 4];
/* 低4位轉(zhuǎn)換成16進制*/
*dst++ = hex[*src & 0xf];
src++;
} else {
/* 不需要轉(zhuǎn)換的字符直接賦值 */
*dst++ = *src++;
}
size--;
}
return (uintptr_t) dst;
}
感謝大神:http://blog.csdn.net/l09711/article/details/46712325
從上面解釋來看当凡,我們只需要*src不轉(zhuǎn)換16進制就可以山害。
4. 解決方法
源碼文件為:src/http/modules/ngx_http_log_module.c
修改源碼如下圖所示,
然后重新編譯沿量,安裝nginx
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_realip_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre --add-module=../lua-nginx-module-0.10.7
/usr/local/nginx/sbin/nginx -s stop
make -j2 && make install
/usr/local/nginx/sbin/nginx
再次post 數(shù)據(jù)到nginx里
查看日志會發(fā)現(xiàn)中文不在轉(zhuǎn)換16進制了浪慌。
第1-2行,是沒有修改源碼前朴则,向nginx url post數(shù)據(jù)权纤,中文被轉(zhuǎn)換成16進制。
第3-5行乌妒,修改源碼后汹想,中文就不會轉(zhuǎn)換為16進制了。也沒有什么亂碼撤蚊。
至此古掏,遇到得問題已解決,在修改源碼得情況下侦啸,目前還沒有發(fā)現(xiàn)什么影響之處槽唾,如由朋友發(fā)現(xiàn),請聯(lián)系我lework[@]yeah.net
5. 總結(jié)
在遇到錯誤得時候光涂,我們往往不知道該怎么搜索此類答案庞萍,我想大家應該都會把錯誤信息放在搜索引擎中搜索,關(guān)鍵字要隨著搜索得到的信息從而不斷變化忘闻,才能往根源得問題靠近挂绰。在搜索引擎給出的大量信息,要懂得抓取有用的信息,不能忽視已經(jīng)給出問題答案的信息葵蒂,即使信息比較久遠交播。像階段1得情況,我如果仔細閱讀上面得解答信息践付,應該會很快得找到問題所在的根源秦士。