1. 程式人生 > >解決nginx在記錄post資料時 中文字元轉成16進位制的問題

解決nginx在記錄post資料時 中文字元轉成16進位制的問題

1. 問題描述

nginx 在獲取post資料時候,如果是中文,則轉換成16進位制顯示在日誌檔案中,如下圖所示。

Paste_Image.png

日誌格式為: log_format postdata '$remote_addr | $request_body | $resp_body';

此篇文章記錄下解決此次問題的過程。

最新版本解決方式

適合nginx 1.11.8 以上版本

在nginx 1.11.8 以上版本中log_format 增加了escape=json 引數,在配置日誌格式時加上此引數可以不轉義變數內容,官方文件-引數說明

日誌配置

log_format postdata '$remote_addr | $request_body | $resp_body'
; log_format postdata escape=json '$remote_addr | $request_body | $resp_body';

日誌輸出

image.png

第一條日誌是不加escape=json 引數後,log_format輸出的
第二條日誌是加上escape=json 引數後,log_format輸出的

2. 軟體版本

  • 系統 centos 6.7 X86_64
  • nginx 1.11.5
  • lua-nginx-module 0.10.7
  • PHP 5.6.27

3. 收集資訊

收集資訊-階段1:

在遇到此類問題的時候,我們大多是使用搜索引擎搜尋答案,因為這樣來的更快一些。當遇到這個問題的時候,我感覺也無從下手,隨即在google中搜索答案,沒過多久,便找到了同類人,也遇到了這個問題

此次搜尋關鍵字: nginx log 中文 16進位制

Paste_Image.png

這個裡面提到了:

  • 為什麼會出現這個問題?
    
  • 解決辦法
    

當時情況,在大量的搜尋結果下,剛開始沒注意到這裡面的問題,認為這個是openresty的解決辦法。就繼續搜尋資訊了。

收集資訊-階段2:

經過上面得資訊,我們可以得知,nginx現在是把中文字元轉換成16進位制。

所以關鍵字變成了:nginx 不支援中文

從這個關鍵字便發現了下面得資訊

Paste_Image.png

從這裡面獲得了:
- 通過降級nginx來解決問題

這位博主通過過降級nginx 程式來達到支援中文得效果,當時目測這文章是2012年得,比較久遠,而且還需要降級,就沒有嘗試這類方法。

資訊收集-階段3:

這次搜尋解決答案也有一段時間了,突然想起了階段1時發現得解決方法,裡面有個命令可以關閉nginx轉換16進製得命令。隨即搜尋關鍵字改成: nginx log escape characters

通過這個關鍵字找到了下列有用資訊。

Paste_Image.png

從這裡面獲得了:

  • 在2008年得時候,通過這個path,讓不可列印得字元轉成16進位制。
  • attachment.bin 檔案記錄了是哪個原始碼檔案的補丁。

通過檢視這個檔案,發現了 ngx_http_log_escape 這函式是轉換16進位制的。要知道nginx原始碼已經被很多國人都閱讀過,肯定有相關的解釋。

隨即關鍵字變成了: nginx ngx_http_log_escape

通過搜尋發現了下列的原始碼解釋

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表示此符號需要轉換,值為0表示不需要轉換 */
    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 [email protected] */
        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;
        /* 返回需要轉換的字元總數*/
    }

    while (size) {
         /* escape[*src >> 5],escape每一行儲存了32個符號,
         所以右移5位,即除以32就找到src對應的字元儲存在escape的行,
         (1 << (*src & 0x1f))此符號在escape一行中的位置,
         相&結果就是判斷src符號位是否為1,需不需要轉換 */
        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
            *dst++ = '\\';
            *dst++ = 'x';
            /* 一個字元佔一個位元組8位,每4位轉成一個16進製表示 */
            /* 高4位轉換成16進位制 */
            *dst++ = hex[*src >> 4];
            /* 低4位轉換成16進位制*/
            *dst++ = hex[*src & 0xf];
            src++;

        } else {
            /* 不需要轉換的字元直接賦值 */
            *dst++ = *src++;
        }
        size--;
    }

    return (uintptr_t) dst;
}

從上面解釋來看,我們只需要*src不轉換16進位制就可以。

4. 解決方法

原始碼檔案為:src/http/modules/ngx_http_log_module.c

修改原始碼如下圖所示,

Paste_Image.png

然後重新編譯,安裝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 資料到nginx裡

Paste_Image.png

檢視日誌會發現中文不在轉換16進位制了。


Paste_Image.png

第1-2行,是沒有修改原始碼前,向nginx url post資料,中文被轉換成16進位制。
第3-5行,修改原始碼後,中文就不會轉換為16進位制了。也沒有什麼亂碼。

至此,遇到得問題已解決,在修改原始碼得情況下,目前還沒有發現什麼影響之處,如由朋友發現,請聯絡我lework[@]yeah.net

5. 總結

在遇到錯誤得時候,我們往往不知道該怎麼搜尋此類答案,我想大家應該都會把錯誤資訊放在搜尋引擎中搜索,關鍵字要隨著搜尋得到的資訊從而不斷變化,才能往根源得問題靠近。在搜尋引擎給出的大量資訊,要懂得抓取有用的資訊,不能忽視已經給出問題答案的資訊,即使資訊比較久遠。像階段1得情況,我如果仔細閱讀上面得解答資訊,應該會很快得找到問題所在的根源。

相關推薦

解決nginx記錄post資料 中文字元16的問題

1. 問題描述nginx 在獲取post資料時候,如果是中文,則轉換成16進位制顯示在日誌檔案中,如下圖所示。Paste_Image.png日誌格式為: log_format postdata '$remote_addr | $request_body | $resp_body';此篇文章記錄下解決此次問題的

串列埠通訊資料傳送--字元傳送和16傳送--傳送位元組的間隔時間

在計算機中,所有的資料在儲存和運算時都要使用二進位制數表示(因為計算機用高電平和低電平分別表示1和0),例如,像a、b、c、d這樣的52個字母(包括大寫)、以及0、1等數字還有一些常用的符號(例如*、#、@等)在計算機中儲存時也要使用二進位制數來表示,而具體用哪些二進位制數字

Qt中int型化為16後形成QString型,int型資料為負需要轉化為其補碼的操作

Qt的的的中封裝了內部函式可以直接將INT型轉化為16進位制,字串表示 int suanz = 10000; QString str = QString("%1").

C# 輸入字元十六字元

private string StringToHexString(string s, Encoding encode) { byte[] b = encode.GetBytes(s);//按照指定編碼將string程式設計位元組陣列

bmp圖片轉換16資料

最近在開發中要在aboot中顯示一張圖片。但是發現aboot中顯示圖片不是直接拿圖片檔案來顯示的,而是把一個16進位制的資料序列依次往螢幕上搬運,就可以了。 那問題是,怎麼把一張圖片轉換成16進位制的資料序列? 在網上也找了一些資料,也諮詢了一些同事,最後終於搞定,下

Java中文和字母與16ASCII碼的轉換

沒事兒看了看這個,寫倆方法方便以後用 /** * 字串中每個字母轉化為16進位制 * @param letter * @return */ public static String

串列埠字元十六

int strHex2int(char *inBuf,unsigned char *sendBuf,int len) {     int index=0;     int resdex=0;     memset(sendBuf,0,sizeof(unsigned char

VB 串列埠傳送,將文字中的字串化成16資料傳送

VB寫一個串列埠傳送程式碼,遇到一個問題,如何將文字中的字串轉化成16進位制資料傳送。 Dim bindate(200) As String Dim senddata As String ‘定義輸入字串變

scala 樣例程式碼 BinToHex 將二進位制檔案轉換16字元

如題: 練習程式碼, 詳細在Github上scala 案例下面 package info.aoye import java.io.{File, FileInputStream} import j

使用web.py接收post資料中文變成xxxx;的問題

使用web.py接收post資料時中文變成&#xxxxx;,英文不變,如下所示。 # post處理函式部分程式碼 def POST(self): print '<Handle>post' webData = we

Redis中get值中文顯示為\xe4\xbd\xa0\xe5\xa5\xbd的16字串怎麼解決

場景: 在伺服器上redis-cli其他(線上)伺服器中redis值時,遇到了這個問題,百度一下,果然有前人採坑,果斷收錄一下_ 在啟動Redis客戶端如下加入引數輸入可解決: [[email protected] redis]# ./bin/redis-cli --raw

Android 16中文(解決出現亂碼問題)

今天在專案裡面要把16進位制的字串轉換為中文,但是轉換的都是亂碼,後來又把轉換函式放在java專案裡面能夠正常轉換,一般出現亂碼第一想到的就是編碼方式,我之前一直是用的utf-8,查了資料,看見有人說utf-8沒有起作用,要用GB2312,我換了一下,果然有效。程式碼如下:

字元陣列16輸出方法

函式實現: static void LOGHEX(const char *pszPrompt, uchar *psParaInfo, int iParaLen) { int i; LOG("%s", pszPrompt); for(i = 0; i < iParaLen;

Script中16Unicode編碼與中文的相互轉換

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">   <HTML>    

轉義字元與十六轉義字元

一般形式 在C中有兩種特殊的字元,八進位制轉義字元和十六進位制轉義字元,八進位制字元的一般形式是'\ddd',d是0-9的數字。十六進位制字元的一般形式是'\xhh',h是0-9或A-F內的一個。八進位制字元和十六進位制字元表示的是字元的ASCII碼對應的數值。比如 '\063'表示的是字元

python2/3中 將base64資料圖片,並將圖片資料轉為16資料的方法、bytes/string的區別

1.python2將base64資料寫成圖片,並將資料轉為16進位制字串的方法 import binascii img = u'R0lGODlhagAeAIcAAAAAAAAARAAAiAAAzABEAABERABEiABEzACIAACIRACIiACIzADMAADMRADMiADMzADd3

Socket網路程式設計之以16模式傳送資料

在一些Socket測試工具,有一個功能叫做“HEX模式”,比如下面這張圖裡的Socket工具: 這次做專案碰巧需要實現一個類似的功能,程式碼如下: public byte[] ConvertHexStrToByteArray(string hexStr) { st

把帶中文的字串轉為 /u16 的 Unicode 碼

話不多說直接上程式碼: /** * 帶中文的字串轉為 /u16進位制 的 Unicode 碼 */ private String tfToHex(String str){ Integer.toHexString(0); //匹配單字元是否中文的正則 String r

8轉義字元16轉義字元的相關問題

8進位制轉義字元:\ddd 16進位制轉義字元:\xddd。一般給出的是2位16進位制,但實際上是三位。但因為第三位沒有實際意義,所以通常省略。 如果結果值超出的表示字元的範圍,此時結果就是未定義的(字元)。在vs中就會出現C2022錯誤,gcc會給出警告。 #defi

資料結構用順序棧實現R轉換

#include<stdio.h> #define MAXSIZE 500 typedef struct{ int *base; int *top; int stacksize; }Sqstack; int Initstack(Sqstack &S)