您当前的位置:首页 > Linux 网站首页Linux

怎样转换nginx日志中的16进制内容为可打印字符

转载请注明出处:黄桂林的博客 huangguilin.com

继之前的文章使用ngx_lua记录nginx的Response
最终我们记录的日志是这个样的:

58.56.46.xx - [27/Jun/2017:17:11:03 +0800] [200] -  api.xxx.com GET /api/ HTTP/1.1 - RESPONSE:{'code':200010,'msg':'\xE6\x9C\xAA\xE8\x8E\xB7\xE5\x8F\x96\xE5\x88\xB0\xE7\x94\xA8\xE6\x88\xB7\xE7\x9A\x84id\xE4\xBF\xA1\xE6\x81\xAF'}
58.56.46.xx - [27/Jun/2017:17:11:03 +0800] [200] -  api.xxx.com GET /api/ HTTP/1.1 - RESPONSE:{'code':200010,'msg':'\xE6\x9C\xAA\xE8\x8E\xB7\xE5\x8F\x96\xE5\x88\xB0\xE7\x94\xA8\xE6\x88\xB7\xE7\x9A\x84id\xE4\xBF\xA1\xE6\x81\xAF'}

实际中也有很多这样的日志

辣么,我们怎么识别这些十六进制的内容呢,好快速的debug,查看接口的输出是否正确。

本文至少介绍三个方法,暂时写一个最简单和一劳永逸的办法。

一、修改nginx源码 (一劳永逸)

我们知道,我们使用的是nginx,nginx的日志是nginx程序创建的,那么伟大的nginx又是开源的,然后呢?

然后继续之前文章中的路径/tmp/nginx-1.11.2/src/http/modules/ngx_http_log_module.c 就可以看到如下的代码,这个是nginx记录日志的源码:

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 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;
        /* 返回需要转换的字符总数*/
    }

    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;
}所  以,

我们需要对代码进行如下修改

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";*/

    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;
    }

    while (size) {
       /*
        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
            *dst++ = '\\';
            *dst++ = 'x';
            *dst++ = hex[*src >> 4];
            *dst++ = hex[*src & 0xf];
            src++;

        } else {
            *dst++ = *src++;
        }
        */
        *dst++ = *src++;
        size--;
    }

    return (uintptr_t) dst;
}

首先注释的是这一段,非常好理解,将转换注释掉,只执行else的部分

       /*
        if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
            *dst++ = '\\';
            *dst++ = 'x';
            *dst++ = hex[*src >> 4];
            *dst++ = hex[*src & 0xf];
            src++;

        } else {
            *dst++ = *src++;
        }
        */

但 是,注释还是不够的,编译还是会报错,因为上方的的hex就没有用了,会报错

src/http/modules/ngx_http_log_module.c: In function ‘ngx_http_log_escape’:
src/http/modules/ngx_http_log_module.c:975:21: error: unused variable ‘hex’ [-Werror=unused-variable]

所以再注释之,编译即可。

安装完成之后,我们再看一下日志中记录的内容已经变成了可打印字符。不再是16进制这种让人类无法直接看懂的内容了。

二、外部shell文件处理

后续再写

三、外部php脚本处理

后续再写

其他任意你喜欢的脚本处理

转载请注明出处:黄桂林的博客
转载请注明出处:黄桂林的博客

关键字nginx / log / nginx log / hex /