Nginx日志模块 通过解析error_log配置项将不同等级的日志信息输出到指定的文件中。nginx启动过程中在解析配置文件时遇到error_log 配置项就调用errlog模块的ngx_error_log函数来解析。 ngx_error_log函数将error_log配置项的值保存在ngx_cycle->new_log成员中,当配置文件中有多条error_log配置项生效时,通过ngx_cycle->new_log.next成员将它们组织起来。 如果配置文件中没有error_log配置项,在配置文件解析完后调用errlog模块的ngx_log_open_default函数将日志等级默认置为NGX_LOG_ERR,日志文件设置为NGX_ERROR_LOG_PATH(该宏是在configure时指定的)。
由此可看无论配置文件中是否有error_log配置项始终会有日志输出,nginx中禁止输出日志唯一的办法:
stderr (0)>= emerg(1) >= alert(2) >= crit(3) >= err(4)>= warn(5) >= notice(6) >= info(7) >= debug(8)
debug级别最低,stderr级别最高;圆括号中的数据是对应日志等级的值。
2,error_log配置项
error_log file | stderr |syslog:server=address[,parameter=value] [debug| info | notice | warn | error | crit | alert | emerg | debug_core |debug_alloc | debug_mutex | debug_event | debug_http | debug_mail | debug_mysql];
默认: error_log logs/error.logs error
error_log logs/err.logs err
所有大于等于err等级的日志信息输出到logs/err.logs文件中,即err、crit、alert、emerg、stderr日志信息被输出。
error_log logs/warn.logs warn
error_log logs/alert.logs alert
当日志级别大于alert时会同时输出到warn.logs及alert.logs文件中,当日志级别介于alert到warn之间时只输出到warn.logs文件中。当日志级别低于warn时不会输出。
复制代码代码示例:
struct ngx_log_s {
ngx_uint_t log_level; //日志等级 ngx_open_file_t *file; // 记录日志文件信息 ngx_atomic_uint_t connection; //引用该日志对象的连接数 ngx_log_handler_pt handler; //输出日志时的回调函数,日志等级不为debug时有效。 void *data; //配合handler成员使用,给handler回调函数传数据 /* * we declare "action" as "char *" because the actions are usually * the static strings and in the "u_char *" case we have to override * their types all the time */ char *action; //记录日志前,nginx当前的动作(即正在做什么) ngx_log_t *next; //指向下一个日志对象 };
4,常用的日志输出接口:
复制代码代码示例:
#define ngx_log_error(level, log, ...) \
if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__) void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const charchar *fmt, ...); #define ngx_log_debug(level, log, ...) \ if ((log)->log_level & level) \ ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__) ngx_log_error宏先判断传入的level是否小于ngx_cycle->new_log.log_level(当存在多个log对象时new_log保存的是优先级最低的对象,即等级值最大的对象)。如果level小于就不需要输出日志。
ngx_log_error_core函数参数:
复制代码代码示例:
/***nginx在启动时解析完配置文件后调用该函数***/
ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle) { static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); /***配置文件中不存在生效的error_log配置项时new_log.file就为空***/ if (cycle->new_log.file == NULL) { cycle->new_log.file = ngx_conf_open_file(cycle, &error_log); if (cycle->new_log.file == NULL) { return NGX_ERROR; } cycle->new_log.log_level = NGX_LOG_ERR; } return NGX_OK; }
6,ngx_error_log函数:
复制代码代码示例:
/***在配置文件中每发现一条error_log配置项该函数被调用一次***/
static charchar * ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, voidvoid *conf) { ngx_log_t *dummy; //new_log用于保存优先级最低的日志对象 dummy = &cf->cycle->new_log; return ngx_log_set_log(cf, &dummy); }
7,ngx_log_set_log函数:
复制代码代码示例:
charchar *
ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head) { ngx_log_t *new_log; ngx_str_t *value, name; /***第一次被调用时log_level ==0 ,后续被调用时需重新申请日志对象***/ if (*head != NULL && (*head)->log_level == 0) { new_log = *head; } else { new_log = ngx_pcalloc(cf->pool, sizeof(ngx_log_t)); if (new_log == NULL) { return NGX_CONF_ERROR; } if (*head == NULL) { *head = new_log; } } /***elts保存有error_log配置项参数,value[0] == "error_log", ...***/ value = cf->args->elts; /***日志信息将输出到屏幕***/ if (ngx_strcmp(value[1].data, "stderr") == 0) { ngx_str_null(&name); cf->cycle->log_use_stderr = 1; } else { /***取出文件名***/ name = value[1]; } /***分配一个文件对象。ngx_cycle->open_files成员中保存有已经打开的文件对象,如果name文件已打开直接返 回该对象,否则重新申请。***/ new_log->file = ngx_conf_open_file(cf->cycle, &name); if (new_log->file == NULL) { return NGX_CONF_ERROR; } /***根据error_log配置项设置日志等级***/ if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) { return NGX_CONF_ERROR; } /***将日志对象插入到ngx_cycle->new_log队列中***/ if (*head != new_log) { ngx_log_insert(*head, new_log); } return NGX_CONF_OK; }
8,ngx_log_set_levels函数:
复制代码代码示例:
static charchar *
ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log) { ngx_uint_t i, n, d, found; ngx_str_t *value; if (cf->args->nelts == 2) { log->log_level = NGX_LOG_ERR; return NGX_CONF_OK; } value = cf->args->elts; for (i = 2; i < cf->args->nelts; i++) { found = 0; /***匹配日志等级***/ for (n = 1; n <= NGX_LOG_DEBUG; n++) { if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) { if (log->log_level != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate log level \"%V\"", &value[i]); return NGX_CONF_ERROR; } log->log_level = n; found = 1; break; } } /***匹配debug日志类型***/ for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) { if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) { if (log->log_level & ~NGX_LOG_DEBUG_ALL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid log level \"%V\"", &value[i]); return NGX_CONF_ERROR; } log->log_level |= d; found = 1; break; } } /***没有找到,该条error_log配置项语法错误***/ if (!found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid log level \"%V\"", &value[i]); return NGX_CONF_ERROR; } } /***当日志等级为debug时,将该日志对象的等级置为debug all,表示输出所有debug信息***/ if (log->log_level == NGX_LOG_DEBUG) { log->log_level = NGX_LOG_DEBUG_ALL; } return NGX_CONF_OK; }
9,ngx_log_insert函数:
复制代码代码示例:
/***日志对象队列按日志等级从低到高排序***/
(责任编辑:IT)static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log) { ngx_log_t tmp; /***新插入的对象等级小于队列头等级时交换对象值***/ if (new_log->log_level > log->log_level) { /* * list head address is permanent, insert new log after * head and swap its contents with head */ tmp = *log; *log = *new_log; *new_log = tmp; log->next = new_log; return; } /***在列队中找合适的位置,将新对象插入其中***/ while (log->next) { if (new_log->log_level > log->next->log_level) { new_log->next = log->next; log->next = new_log; return; } log = log->next; } log->next = new_log; } |