4.4 error日志的用法

Nginx的日志模块(这里所说的日志模块是ngx_errlog_module模块,而ngx_http_log_module模块是用于记录HTTP请求的访问日志的,两者功能不同,在实现上也没有任何关系)为其他模块提供了基本的记录日志功能,本章提到的mytest模块当然也可以使用日志模块提供的接口。出于跨平台的考虑,日志模块提供了相当多的接口,主要是因为有些平台下不支持可变参数。本节主要讨论支持可变参数的日志接口,事实上不支持可变参数的日志接口在实现方面与其并没有太大的不同(参见表4-9)。首先看一下日志模块对于支持可变参数平台而提供的3个接口。


define ngx_log_error(level,log,args……)

\

if((log)->log_level>=level)ngx_log_error_core(level,log,args)

define ngx_log_debug(level,log,args……)

\

if((log)->log_level&level)

\

ngx_log_error_core(NGX_LOG_DEBUG,log,args)

void ngx_log_error_core(ngx_uint_t level,ngx_log_tlog,ngx_err_t err,const charfmt,……);


Nginx的日志模块记录日志的核心功能是由ngx_log_error_core方法实现的,ngx_log_error宏和ngx_log_debug宏只是对它做了简单的封装,一般情况下记录日志时只需要使用这两个宏。

ngx_log_error宏和ngx_log_debug宏都包括参数level、log、err、fmt,下面分别解释这4个参数的意义。

(1)level参数

对于ngx_log_error宏来说,level表示当前这条日志的级别。它的取值范围见表4-6。

4.4 error日志的用法 - 图1

使用ngx_log_error宏记录日志时,如果传入的level级别小于或等于log参数中的日志级别(通常是由nginx.conf配置文件中指定),就会输出日志内容,否则这条日志会被忽略。

在使用ngx_log_debug宏时,level的意义完全不同,它表达的意义不再是级别(已经是DEBUG级别),而是日志类型,因为ngx_log_debug宏记录的日志必须是NGX_LOG_DEBUG调试级别的,这里的level由各子模块定义。level的取值范围参见表4-7。

4.4 error日志的用法 - 图2

当HTTP模块调用ngx_log_debug宏记录日志时,传入的level参数是NGX_LOG_DEBUG_HTTP,这时如果log参数不属于HTTP模块,如使用了event事件模块的log,则不会输出任何日志。它正是ngx_log_debug拥有level参数的意义所在。

(2)log参数

实际上,在开发HTTP模块时我们并不用关心log参数的构造,因为在处理请求时ngx_http_request_t结构中的connection成员就有一个ngx_log_t类型的log成员,可以传给ngx_log_error宏和ngx_log_debug宏记录日志。在读取配置阶段,ngx_conf_t结构也有log成员可以用来记录日志(读取配置阶段时的日志信息都将输出到控制台屏幕)。下面简单地看一下ngx_log_t的定义。


typedef struct ngx_log_s ngx_log_t;

typedef u_charngx_log_handler_pt)(ngx_log_tlog,u_charbuf,size_t len);

struct ngx_log_s{

//日志级别或者日志类型

ngx_uint_t log_level;

//日志文件

ngx_open_file_t*file;

//连接数,不为0时会输出到日志中

ngx_atomic_uint_t connection;

/记录日志时的回调方法。当handler已经实现(不为NULL),并且不是DEBUG调试级别时,才会调用handler钩子方法/

ngx_log_handler_pt handler;

/每个模块都可以自定义data的使用方法。通常,data参数都是在实现了上面的handler回调方法后才使用的。例如,HTTP框架就定义了handler方法,并在data中放入了这个请求的上下文信息,这样每次输出日志时都会把这个请求URI输出到日志的尾部/

void*data;

/表示当前的动作。实际上,action与data是一样的,只有在实现了handler回调方法后才会使用。例如,HTTP框架就在handler方法中检查action是否为NULL,如果不为NULL,就会在日志后加入“while”+action,以此表示当前日志是在进行什么操作,帮助定位问题/

char*action;

};


可以看到,如果只是想把相应的信息记录到日志文件中,那么完全不需要关心ngx_log_t类型的log参数是如何构造的。特别是在编写HTTP模块时,HTTP框架要求所有的HTTP模块都使用它提供的log,如果重定义ngx_log_t中的handler方法,或者修改data指向的地址,那么很可能会造成一系列问题。

然而,从上文对ngx_log_t结构的描述中可以看出,如果定义一种新的模块(不是HTTP模块),那么日志模块提供很强大的功能,可以把一些通用化的工作都放到handler回调方法中实现。

(3)err参数

err参数就是错误码,一般是执行系统调用失败后取得的errno参数。当err不为0时,Nginx日志模块将会在正常日志内容前输出这个错误码以及其对应的字符串形式的错误消息。

(4)fmt参数

fmt就是可变参数,就像在printf等C语言方法中的输入一样。例如:


ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,

"test_flag=%d,test_str=%V,path=%*s,mycf addr=%p",

mycf->my_flag,

&mycf->my_str,

mycf->my_path->name.len,

mycf->my_path->name.data,

mycf);


fmt的大部分规则与printf等通用可变参数是一致的,然而Nginx为了方便它自定义的数据类型,重新实现了基本的ngx_vslprintf方法。例如,增加了诸如%V等这样的转换类型,%V后可加ngx_str_t类型的变量,这些都是普通的printf中没有的。表4-8列出了ngx_vslprintf中支持的27种转换格式。

注意 printf或者sprintf支持的一些转换格式在ngx_vslprintf中是不支持的,或者意义不同。

4.4 error日志的用法 - 图3

4.4 error日志的用法 - 图4

例如,在4.2.4节自定义的ngx_conf_set_myconfig方法中,可以这样输出日志。


long tl=4900000000;

u_long tul=5000000000;

int32_t ti32=110;

ngx_str_t tstr=ngx_string("teststr");

double tdoub=3.1415926535897932;

int x=15;

ngx_log_error(NGX_LOG_ALERT,cf->log,0,

"l=%l,ul=%ul,D=%D,p=%p,f=%.10f,str=%V,x=%xd,X=%Xd",

tl,tul,ti32,&ti32,tdoub,&tstr,x,x);


上述这段代码将会输出:


nginx:[alert]l=4900000000,ul=5000000000,D=110,p=00007FFFF26B36DC,f=3.1415926536,str=teststr,x=f,X=F


在Nginx的许多核心模块中可以看到,它们多使用的是debug调试级别的日志接口,见表4-9。

4.4 error日志的用法 - 图5