Nginx ngx_log_stderr源码分析(中)

/*
表4-8打印日志或者使用ngx_sprintf系列方法转换字符串时支持的27种转化格式
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃转换格式  ┃    用法                                                                                ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  表示无符号,其后还可以跟其他转换符号,如%ui表示要转换的类型是ngx_uint_t。如果其后没   ┃
┃%U        ┃                                                                                        ┃
┃          ┃有跟转换符号,则表示要转换的类型是无符号十进制正数                                      ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%m        ┃  表示以最大长度来转换数字类型(如int)                                                   ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  以十六进制来格式化转换后的数据。注意,Nginx中的%X与printf等转换格式完全不同,它只     ┃
┃          ┃是限制转换后的数字以十六进制格式来显示,而不是限制相应参数的类型。例如,%Xd后跟着int    ┃
┃%X        ┃                                                                                        ┃
┃          ┃类型,表示以十六进制格式来显示int整数,而%Xp表示以十六进制格式来显示指针地址。如果仅    ┃
┃          ┃有%X,那么是没有任何输出的                                                              ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  %X与%X的用法完全相同,只是%X以A、B、C、D、E、F表示十进制中的10、11、12、13、          ┃
┃%x        ┃                                                                                        ┃
┃          ┃14、15,而%x足以小写的a、b、c、d、e、f来表示                                            ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  其后必须紧跟数字。当前实现版本下必须与%f配合使用,表示转换浮点数时小数部分的位数。例  ┃
┃%.       ┃                                                                                        ┃
┃          ┃如,%.lOf表示转换double类型时,小数点后转换且必须转换为10位,不足IO位以O填补           ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  转换double类型数据。注意,它与printf等标准C语言中的%f完全不同,如果想转换小数部分,   ┃
┃%f        ┃                                                                                        ┃
┃          ┃则必须加上%.(number)f。参见本表中%.的描述                                             ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%+        ┃  表示要转换的字符长度。目前仅与%s配合使用                                              ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  转换1个char~或者u- char~的字符串。与%+配合使用时,%ts表示输出指定长度的字符串,其     ┃
┃%S        ┃后必须有两个参数:表示输出字符串长度的size_t和字符串地址char~类型。如果不与%+配合使用, ┃
┃          ┃而与printf等标准格式相同,那么字符串必须以‘\0’结尾                                   ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%V        ┃                                                                                        ┃
┃          ┃  转换ngx_str_t类型,%V对应的参数必须是ngx_str_t变量的地址。它将会按照ngx_str_t类型的   ┃
┃          ┃len长度来输出data字符串                                                                 ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃          ┃  转换ngx_variable_value_t类型,%V对应的参数必须是ngx_variable_valuej变量的地址。它将会 ┃
┃%V        ┃                                                                                        ┃
┃          ┃按照ngx_variable_value-t粪型的len长度来输出data字符串                                   ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%O        ┃  转换1个offt类型                                                                       ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%P        ┃  转换1个ngx_pid_t类型                                                                  ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%T        ┃  转换1个time—t类型                                                                    ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%M        ┃  转换1个ngx_msec_t类型                                                                 ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%Z        ┃  转换ssize_t类型数据,如果用%UZ,则转换的数据类型是size-t                              ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%i        ┃  转换ngx_int_t型数据,如果用%ui,则转换的数据类型是ngx_uint_t                          ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%d        ┃  转换int型数据,如果用%ud,则转换的数据类型是u_int                                     ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%1        ┃  转换long型数据,如果用%ul,则转换的数据类型是u_long                                   ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%D        ┃  转换int32-t型数据,如果用%uD,则转换的数据类型是uint32-t                              ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%L        ┃  转换int64j型数据,如果用%uL,则转换的数据类型是uint64 t                               ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%A        ┃  转换ngx_atomic_int_t型数据,如果用%uA,则转换的数据类型是ngx_atomic_uint_t            ┃
┗━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃转换格式  ┃    用法                                                                                ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%r        ┃                                                                                        ┃
┃          ┃  转换1个rlimj类型。系统调用getrlimit或者setrlimit时都会使用rlimj类型参数,它实际上是   ┃
┃          ┃一个算术数据类型,等同干类型int、size t或者offt                                         ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%p        ┃  转换1个指针(地址)                                                                   ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%C        ┃  转换1个字符类型                                                                       ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%Z        ┃  表示’\O’                                                                           ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%N        ┃  表示’\n’换行符,即”\xOa”,在windows操作系统上则表示’\r\n’,也就是”\xOd\xOa”  ┃
┣━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃%%        ┃  打印1个百分号(%)                                                                      ┃
┗━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
例如,在4.2.4节自定义的ngx_c onf_set_myc onfig方法中,可以这样输出日志。
long tl = 4900000000;
u_long tul = 5000000000;
int32_t ti32 = 110;
ngx_str_t   tstr  =  ngx_string ( " teststr" ) ;
double   tdoub   =  3.1415 926535897932 ;
int x = 15;
ngx_log_error (NGX_LOG_ALERT , cf->log ,  0 ,
              " l= %l , ul=%ul , D=%D, p=%p, f=%.lOf , str=%V, x=%xd, X=%Xd "
                tl, tul, ti32 , &ti3 2 , tdoub , &tstr, x, x)  ;
土述这段代码将会输出:
      nginx :   [alert]   1=4900000000 , ul=5000000000 , D=110, p=00007FFFF26836DC, f=3 . 1415926536
, str=teststr, x=f , X=F
*/
// 自定义的格式化输出
// buf:存储数据  last:最大的内存地址  fmt:可变参数开头(format)
u_char *
ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
{
    u_char                *p, zero;  // 占位符号 0或者空格
    int                    d;
    double                 f;
    size_t                 len, slen;
    int64_t                i64;// 保存%d
    uint64_t               ui64, frac;// 保存%ud
                    //typedef ngx_uint_t  ngx_rbtree_key_t;
    ngx_msec_t             ms;
    ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n; // 需要用到的临时变
    ngx_str_t             *v;
    ngx_variable_value_t  *vv;
// 	//参考<输入剖析nginx-变量>
// typedef struct {
//     unsigned    len:28;  /* 变量值的长度 */  

//     unsigned    valid:1;   /* 变量是否有效 */  
//     /* 
//       变量是否是可缓存的,一般来说,某些变量在第一次得到变量值后,后面再次用到时,可以直接使用上  
//       而对于一些所谓的no_cacheable的变量,则需要在每次使用的时候,都要通过get_handler之类操作,再次获取  
//     */
//     unsigned    no_cacheable:1;  
            
//     unsigned    not_found:1; /* 变量没有找到,一般是指某个变量没用能够通过get获取到其变量值,见ngx_http_variable_not_found */  
//     unsigned    escape:1;  /* 变量值是否需要作转义处理*/  

//     u_char     *data;  /* 变量值 */  
// } ngx_variable_value_t;


    while (*fmt && buf < last) {

        /*
         * "buf < last" means that we could copy at least one character:
         * the plain character, "%%", "%c", and minus without the checking
         */

        if (*fmt == '%') {//代表需要处理的格式=

            i64 = 0;       // 保存一些数字
            ui64 = 0;         // 保存一些数字
            //zero代表填充符是空格还是零
            zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
            width = 0;   // 用于判断需要占位的宽度(比如%5d 不够5位空格填补) 目前只对%d和%f有效
            sign = 1;      // 标记有无符号类型 如果是%u就设为0
            hex = 0;          // 是否以16进制显示 0:不是 1:是,且以小写字母显示 2:是,且以大写字母显示
            max_width = 0;
            frac_width = 0;  // 小数点之后的数字 如%.2f frac_width=2
            slen = (size_t) -1; //字符串长度

            while (*fmt >= '0' && *fmt <= '9') {//计算宽度
                width = width * 10 + *fmt++ - '0';
            }


            for ( ;; ) {
                switch (*fmt) {

                case 'u':
				   //无符号整数
// 				   表示无符号,其后还可以跟其他转换符号,如%ui表示要转换的类型是ngx_uint_t。如果其后没   ┃
// ┃%U        ┃                                                                                        ┃
// ┃          ┃有跟转换符号,则表示要转换的类型是无符号十进制正数                       
                    sign = 0;
                    fmt++;
                    continue;

                case 'm':
				   //%m        ┃  表示以最大长度来转换数字类型(如int) 
                    max_width = 1;
                    fmt++;
                    continue;

                case 'X':
// 				          ┃  以十六进制来格式化转换后的数据。注意,Nginx中的%X与printf等转换格式完全不同,它只     ┃
// ┃          ┃是限制转换后的数字以十六进制格式来显示,而不是限制相应参数的类型。例如,%Xd后跟着int    ┃
// ┃%X        ┃                                                                                        ┃
// ┃          ┃类型,表示以十六进制格式来显示int整数,而%Xp表示以十六进制格式来显示指针地址。如果仅    ┃
// ┃          ┃有%X,那么是没有任何输出的                        
                    hex = 2;
                    sign = 0;
                    fmt++;
                    continue;

                case 'x':
// 				          ┃  %X与%X的用法完全相同,只是%X以A、B、C、D、E、F表示十进制中的10、11、12、13、          ┃
// ┃%x        ┃                                                                                        ┃
// ┃          ┃14、15,而%x足以小写的a、b、c、d、e、f来表示   
                    hex = 1;
                    sign = 0;
                    fmt++;
                    continue;

                case '.':
				//计算小数位数
                    fmt++;

                    while (*fmt >= '0' && *fmt <= '9') {
                        frac_width = frac_width * 10 + *fmt++ - '0';
                    }
                //此时后面只有f代表小数
                    break;

                case '*':
                    slen = va_arg(args, size_t);
                    fmt++;
                    continue;

                default:
                    break;
                }

                break;
            }

           // 判断剩下的一些类型 % d s p f
            switch (*fmt) {

            case 'V': //输出ngx_str_t类型数据
                v = va_arg(args, ngx_str_t *);
                if(v == NULL)
                    continue;
                //获取最小可写入字符数
                len = ngx_min(((size_t) (last - buf)), v->len);
                buf = ngx_cpymem(buf, v->data, len);
                fmt++;

                continue;

            case 'v':
                vv = va_arg(args, ngx_variable_value_t *);

                len = ngx_min(((size_t) (last - buf)), vv->len);
                buf = ngx_cpymem(buf, vv->data, len);
                fmt++;

                continue;

            case 's':
                p = va_arg(args, u_char *);

                if (slen == (size_t) -1) {
                    while (*p && buf < last) {
                        *buf++ = *p++;
                    }

                } else {
                    len = ngx_min(((size_t) (last - buf)), slen);
                    buf = ngx_cpymem(buf, p, len);
                }

                fmt++;

                continue;

            case 'O':
                i64 = (int64_t) va_arg(args, off_t);
                sign = 1;
                break;

            case 'P':
                 //typedef pid_t       ngx_pid_t;   其实就代表int类型
                i64 = (int64_t) va_arg(args, ngx_pid_t);
                sign = 1;
                break;

            case 'T':
			//typdef long time_t
                i64 = (int64_t) va_arg(args, time_t);
                sign = 1;
                break;

            case 'M': //打印时间,变量参数类型为ngx_msec_t
                ms = (ngx_msec_t) va_arg(args, ngx_msec_t);
                if ((ngx_msec_int_t) ms == -1) {
                    sign = 1;
                    i64 = -1;
                } else {
                    sign = 0;
                    ui64 = (uint64_t) ms;
                }
                break;

            case 'z':
			//ssize_t是linux下的一个有符号的size_t
                if (sign) {
                    i64 = (int64_t) va_arg(args, ssize_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, size_t);
                }
                break;

            case 'i':
			//%i        ┃  转换ngx_int_t型数据,如果用%ui,则转换的数据类型是ngx_uint_t
			//typedef intptr_t        ngx_int_t;
			//typedef uintptr_t       ngx_uint_t;
                if (sign) {
                    i64 = (int64_t) va_arg(args, ngx_int_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, ngx_uint_t);
                }

                if (max_width) {
                    width = NGX_INT_T_LEN;
                }

                break;

            case 'd':
			  //整形
                if (sign) {
                    i64 = (int64_t) va_arg(args, int);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_int);
                }
                break;

            case 'l':
			  //长整形
                if (sign) {
                    i64 = (int64_t) va_arg(args, long);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_long);
                }
                break;

            case 'D':
			  //短整形
                if (sign) {
                    i64 = (int64_t) va_arg(args, int32_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, uint32_t);
                }
                break;

            case 'L':
			  //长整形
                if (sign) {
                    i64 = va_arg(args, int64_t);
                } else {
                    ui64 = va_arg(args, uint64_t);
                }
                break;

            case 'A':
			  // typedef long                        ngx_atomic_int_t;
                if (sign) {
                    i64 = (int64_t) va_arg(args, ngx_atomic_int_t);
                } else {
					//typedef unsigned long               ngx_atomic_uint_t;
                    ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);
                }

                if (max_width) {
					//#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)
                    width = NGX_ATOMIC_T_LEN;
                }

                break;

            case 'f':
                f = va_arg(args, double);

                if (f < 0) {
                    *buf++ = '-';
                    f = -f;
                }
               //取出整数位
                ui64 = (int64_t) f;
                frac = 0;

                if (frac_width) {

                    scale = 1;
                    for (n = frac_width; n; n--) {
                        scale *= 10;
                    }
					// uint64_t               ui64, frac;// 保存%ud
                    frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);
                    
                    if (frac == scale) {
                        ui64++;
                        frac = 0;
                    }
                }

                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);

                if (frac_width) {
                    if (buf < last) {
                        *buf++ = '.';
                    }

                    buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);
                }

                fmt++;
                continue;
            case 'p':
                ui64 = (uintptr_t) va_arg(args, void *);
                hex = 2;
                sign = 0;
                zero = '0';
				//NIG_PTR_SIZE即为机器字长
                width = NGX_PTR_SIZE * 2;
                break;

            case 'c':
                d = va_arg(args, int);
                *buf++ = (u_char) (d & 0xff);
                fmt++;

                continue;

            case 'Z':
                *buf++ = '\0';
                fmt++;

                continue;

            case 'N':
#if (NGX_WIN32)
                *buf++ = CR;
                if (buf < last) {
                    *buf++ = LF;
                }
#else
                *buf++ = LF;
#endif
                fmt++;

                continue;

            case '%':
                *buf++ = '%';
                fmt++;

                continue;

            default:
                *buf++ = *fmt++;

                continue;
            }
            //无论是正数负数都被转换成uint64输出
            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (uint64_t) -i64;

                } else {
                    ui64 = (uint64_t) i64;
                }
            }

            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

            fmt++;

        } else {
			//普通字符
            *buf++ = *fmt++;
        }
    }

    return buf;
}

u_char *
ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)
{
    ngx_str_t  *msg;

    msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]:
                                              &ngx_unknown_error;
    size = ngx_min(size, msg->len);

    return ngx_cpymem(errstr, msg->data, size);
}
u_char * ngx_cdecl
ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)
{
    u_char   *p;
    va_list   args;

    va_start(args, fmt);
    p = ngx_vslprintf(buf, last, fmt, args);
    va_end(args);

    return p;
}



这个函数主要运用于往指定缓冲区中写入格式字符串

常用类型定义

#define NGX_INT32_LEN   (sizeof("-2147483648") - 1)
#define NGX_INT64_LEN   (sizeof("-9223372036854775808") - 1)

#if (NGX_PTR_SIZE == 4)
#define NGX_INT_T_LEN   NGX_INT32_LEN
#define NGX_MAX_INT_T_VALUE  2147483647

#else
#define NGX_INT_T_LEN   NGX_INT64_LEN
#define NGX_MAX_INT_T_VALUE  9223372036854775807
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值