/*
表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