leevis.com
leevis.com copied to clipboard
nginx的error log代码分析
概述
error log 记录了nginx系统日志,区别于业务日志。
封装成ngx_errlog_module模块,代码在文件core/ngx_log.h|c中。
ngx_errlog_module 属于核心模块NGX_CORE_MODULE,提供了error_log配置指令。
代码分析
涉及到的结构体:
// 封装打开的文件结构体 定义在core/ngx_conf_file.h 文件中
struct ngx_open_file_s {
ngx_fd_t fd; // 打开的文件描述符
ngx_str_t name;
void (*flush)(ngx_open_file_t *file, ngx_log_t *log);
void *data;
};
struct ngx_log_s {
ngx_uint_t log_level; // 错误日志等级
ngx_open_file_t *file; // 打开的文件描述结构体
// 链接的个数下标
ngx_atomic_uint_t connection;
time_t disk_full_time;
ngx_log_handler_pt handler;
void *data;
// 写回调ngx_syslog_writer
ngx_log_writer_pt writer;
// ngx_syslog_peer_t
void *wdata;
/*
* 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;
// 形成一个链表
ngx_log_t *next;
};
ngx_log.c文件中定义了两个静态变量,static限制了变量的作用域和保存范围。
static ngx_log_t ngx_log;
static ngx_open_file_t ngx_log_file;
通过ngx_error_log函数解析error_log配置指令。
涉及到的函数:
ngx_log_t *ngx_log_init(u_char *prefix); // 初始化定义的静态日志文件结构体
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...); //
ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle); // 没有配置error log,则打开默认error log
static char *
ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_log_t *dummy;
dummy = &cf->cycle->new_log;
return ngx_log_set_log(cf, &dummy);
}
char *
ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head)
{
ngx_log_t *new_log;
ngx_str_t *value, name;
ngx_syslog_peer_t *peer;
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;
}
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "stderr") == 0) {
ngx_str_null(&name);
cf->cycle->log_use_stderr = 1;
new_log->file = ngx_conf_open_file(cf->cycle, &name);
if (new_log->file == NULL) {
return NGX_CONF_ERROR;
}
} else if (ngx_strncmp(value[1].data, "memory:", 7) == 0) {
#if (NGX_DEBUG)
size_t size, needed;
ngx_pool_cleanup_t *cln;
ngx_log_memory_buf_t *buf;
value[1].len -= 7;
value[1].data += 7;
needed = sizeof("MEMLOG :" NGX_LINEFEED)
+ cf->conf_file->file.name.len
+ NGX_SIZE_T_LEN
+ NGX_INT_T_LEN
+ NGX_MAX_ERROR_STR;
size = ngx_parse_size(&value[1]);
if (size == (size_t) NGX_ERROR || size < needed) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid buffer size \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
buf = ngx_pcalloc(cf->pool, sizeof(ngx_log_memory_buf_t));
if (buf == NULL) {
return NGX_CONF_ERROR;
}
buf->start = ngx_pnalloc(cf->pool, size);
if (buf->start == NULL) {
return NGX_CONF_ERROR;
}
buf->end = buf->start + size;
buf->pos = ngx_slprintf(buf->start, buf->end, "MEMLOG %uz %V:%ui%N",
size, &cf->conf_file->file.name,
cf->conf_file->line);
ngx_memset(buf->pos, ' ', buf->end - buf->pos);
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_CONF_ERROR;
}
cln->data = new_log;
cln->handler = ngx_log_memory_cleanup;
new_log->writer = ngx_log_memory_writer;
new_log->wdata = buf;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"nginx was built without debug support");
return NGX_CONF_ERROR;
#endif
} else if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
if (peer == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
new_log->writer = ngx_syslog_writer;
new_log->wdata = peer;
} else {
new_log->file = ngx_conf_open_file(cf->cycle, &value[1]);
if (new_log->file == NULL) {
return NGX_CONF_ERROR;
}
}
if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
if (*head != new_log) {
ngx_log_insert(*head, new_log);
}
return NGX_CONF_OK;
}
void
ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
const char *fmt, va_list args)
{
u_char *p, *last, *msg;
ssize_t n;
ngx_uint_t wrote_stderr, debug_connection;
u_char errstr[NGX_MAX_ERROR_STR];
// 2k的buffer
last = errstr + NGX_MAX_ERROR_STR;
// 日期时间
p = ngx_cpymem(errstr, ngx_cached_err_log_time.data,
ngx_cached_err_log_time.len);
// 日志等级
p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);
/* pid#tid */
p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
ngx_log_pid, ngx_log_tid);
if (log->connection) {
p = ngx_slprintf(p, last, "*%uA ", log->connection);
}
msg = p; // 真正的错误消息的开始
// 错误消息
p = ngx_vslprintf(p, last, fmt, args);
if (err) {
// 发生错误,会展示系统错误
p = ngx_log_errno(p, last, err);
}
if (level != NGX_LOG_DEBUG && log->handler) {
p = log->handler(log, p, last - p);
}
if (p > last - NGX_LINEFEED_SIZE) {
p = last - NGX_LINEFEED_SIZE;
}
// 添加回车换行
ngx_linefeed(p);
wrote_stderr = 0;
debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0;
while (log) {
if (log->log_level < level && !debug_connection) {
break;
}
if (log->writer) {
log->writer(log, level, errstr, p - errstr);
goto next;
}
if (ngx_time() == log->disk_full_time) {
goto next;
}
n = ngx_write_fd(log->file->fd, errstr, p - errstr);
if (n == -1 && ngx_errno == NGX_ENOSPC) {
log->disk_full_time = ngx_time();
}
if (log->file->fd == ngx_stderr) {
wrote_stderr = 1;
}
next:
log = log->next;
}
if (!ngx_use_stderr
|| level > NGX_LOG_WARN
|| wrote_stderr)
{
return;
}
// 需要向标准错误输出日志
// 用“nginx:[等级]”替换掉开始的“时间 [等级] tid#pid 等
msg -= (7 + err_levels[level].len + 3);
(void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]);
(void) ngx_write_console(ngx_stderr, msg, p - msg);
}