Nginx 日志分析

来源:互联网 发布:苏联政治笑话 知乎 编辑:程序博客网 时间:2024/05/17 08:38

日志使用

  •   access_log的最小作用域是location, 关闭方式是:access_log off;

  •   error_log的最小作用域是http,关闭方式是:error_log /dev/null;

  •   示例:error_log  logs/errord.log debug;

源码分析

Nginx启动由main开始执行,在nginx.c的278行,调用ngx_log_init(ngx_prefix),如下:

    log = ngx_log_init(ngx_prefix);

其中【ngx_log.c】ngx_log_init(ngx_prefix)代码如下【主要实现了创建日志文件/usr/local/logs/error.log】

ngx_log_t *ngx_log_init(u_char *prefix){    u_char  *p, *name;    size_t   nlen, plen;    ngx_log.file = &ngx_log_file;    ngx_log.log_level = NGX_LOG_NOTICE;    name = (u_char *) NGX_ERROR_LOG_PATH;    /*     * we use ngx_strlen() here since BCC warns about     * condition is always false and unreachable code     */    nlen = ngx_strlen(name);    if (nlen == 0) {        ngx_log_file.fd = ngx_stderr;        return &ngx_log;    }    p = NULL;#if (NGX_WIN32)    if (name[1] != ':') {#else    if (name[0] != '/') {#endif        if (prefix) {            plen = ngx_strlen(prefix);        } else {#ifdef NGX_PREFIX            prefix = (u_char *) NGX_PREFIX;            plen = ngx_strlen(prefix);#else            plen = 0;#endif        }        if (plen) {            name = malloc(plen + nlen + 2);            if (name == NULL) {                return NULL;            }            p = ngx_cpymem(name, prefix, plen);            if (!ngx_path_separator(*(p - 1))) {                *p++ = '/';            }            ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);            p = name;        }    }    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,                                    NGX_FILE_CREATE_OR_OPEN,                                    NGX_FILE_DEFAULT_ACCESS);    if (ngx_log_file.fd == NGX_INVALID_FILE) {        ngx_log_stderr(ngx_errno,                       "[alert] could not open error log file: "                       ngx_open_file_n " \"%s\" failed", name);#if (NGX_WIN32)        ngx_event_log(ngx_errno,                       "could not open error log file: "                       ngx_open_file_n " \"%s\" failed", name);#endif        ngx_log_file.fd = ngx_stderr;    }    if (p) {        ngx_free(p);    }    return &ngx_log;}


在之后的解析配置文件nginx.conf的过程中

static ngx_command_t  ngx_errlog_commands[] = {    {ngx_string("error_log"),     NGX_MAIN_CONF|NGX_CONF_1MORE,     ngx_error_log,     0,     0,     NULL},    ngx_null_command};static ngx_core_module_t  ngx_errlog_module_ctx = {    ngx_string("errlog"),    NULL,    NULL};ngx_module_t  ngx_errlog_module = {    NGX_MODULE_V1,    &ngx_errlog_module_ctx,                /* module context */    ngx_errlog_commands,                   /* module directives */    NGX_CORE_MODULE,                       /* module type */    NULL,                                  /* init master */    NULL,                                  /* init module */    NULL,                                  /* init process */    NULL,                                  /* init thread */    NULL,                                  /* exit thread */    NULL,                                  /* exit process */    NULL,                                  /* exit master */    NGX_MODULE_V1_PADDING};


其中ngx_error_log.c的代码如下【主要实现了按照配置文件的命令创建新的日志文件和设置日志输出的等级】

char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log){    ngx_uint_t   i, n, d, found;    ngx_str_t   *value;    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;            }        }          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) {  //若已设置err_levels,则不允许设置debug level。  //即error_log  logs/error.log  DEBUG NGX_LOG_DEBUG;将报错。                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;            }        }        if (!found) {            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,                               "invalid log level \"%V\"", &value[i]);            return NGX_CONF_ERROR;        }    }    // 如果error_log  logs/error.log NGX_LOG_DEBUG;相当于设置了全部的debug level.    if (log->log_level == NGX_LOG_DEBUG) {        log->log_level = NGX_LOG_DEBUG_ALL;    }    return NGX_CONF_OK;}static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ngx_str_t  *value, name;    if (cf->cycle->new_log.file) {        return "is duplicate";    }    value = cf->args->elts;    if (ngx_strcmp(value[1].data, "stderr") == 0) {        ngx_str_null(&name);    } else {        name = value[1];    }    cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name);    if (cf->cycle->new_log.file == NULL) {        return NULL;    }    if (cf->args->nelts == 2) {        cf->cycle->new_log.log_level = NGX_LOG_ERR;        return NGX_CONF_OK;    }    cf->cycle->new_log.log_level = 0;    return ngx_log_set_levels(cf, &cf->cycle->new_log);}

若配置文件中没有配置使用error_log,则【ngx_cycle.c】如下代码,作用生成新的new_log。

(此处发现ngx_cycle_s中的new_log仅仅是为了记录新日志文件之用;另外为了记录日志初始化之前的日志,所以ngx_log_init中创建/usr/local/logs/error.log,若配置文件中重新配置,则以配置文件为准;若没有配置,则就以此文件记录日志

    if (cycle->new_log.file == NULL) {        cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);        if (cycle->new_log.file == NULL) {            goto failed;        }    }

最后,

    cycle->log = &cycle->new_log;    pool->log = &cycle->new_log;

至此日志可正常使用。


日志使用
  •   access_log的最小作用域是location, 关闭方式是:access_log off;

  •   error_log的最小作用域是http,关闭方式是:error_log /dev/null;

  •   示例:error_log  logs/errord.log debug;

源码分析

Nginx启动由main开始执行,在nginx.c的278行,调用ngx_log_init(ngx_prefix),如下:

    log = ngx_log_init(ngx_prefix);

其中【ngx_log.c】ngx_log_init(ngx_prefix)代码如下【主要实现了创建日志文件/usr/local/logs/error.log】

ngx_log_t *ngx_log_init(u_char *prefix){    u_char  *p, *name;    size_t   nlen, plen;    ngx_log.file = &ngx_log_file;    ngx_log.log_level = NGX_LOG_NOTICE;    name = (u_char *) NGX_ERROR_LOG_PATH;    /*     * we use ngx_strlen() here since BCC warns about     * condition is always false and unreachable code     */    nlen = ngx_strlen(name);    if (nlen == 0) {        ngx_log_file.fd = ngx_stderr;        return &ngx_log;    }    p = NULL;#if (NGX_WIN32)    if (name[1] != ':') {#else    if (name[0] != '/') {#endif        if (prefix) {            plen = ngx_strlen(prefix);        } else {#ifdef NGX_PREFIX            prefix = (u_char *) NGX_PREFIX;            plen = ngx_strlen(prefix);#else            plen = 0;#endif        }        if (plen) {            name = malloc(plen + nlen + 2);            if (name == NULL) {                return NULL;            }            p = ngx_cpymem(name, prefix, plen);            if (!ngx_path_separator(*(p - 1))) {                *p++ = '/';            }            ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);            p = name;        }    }    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,                                    NGX_FILE_CREATE_OR_OPEN,                                    NGX_FILE_DEFAULT_ACCESS);    if (ngx_log_file.fd == NGX_INVALID_FILE) {        ngx_log_stderr(ngx_errno,                       "[alert] could not open error log file: "                       ngx_open_file_n " \"%s\" failed", name);#if (NGX_WIN32)        ngx_event_log(ngx_errno,                       "could not open error log file: "                       ngx_open_file_n " \"%s\" failed", name);#endif        ngx_log_file.fd = ngx_stderr;    }    if (p) {        ngx_free(p);    }    return &ngx_log;}


在之后的解析配置文件nginx.conf的过程中

static ngx_command_t  ngx_errlog_commands[] = {    {ngx_string("error_log"),     NGX_MAIN_CONF|NGX_CONF_1MORE,     ngx_error_log,     0,     0,     NULL},    ngx_null_command};static ngx_core_module_t  ngx_errlog_module_ctx = {    ngx_string("errlog"),    NULL,    NULL};ngx_module_t  ngx_errlog_module = {    NGX_MODULE_V1,    &ngx_errlog_module_ctx,                /* module context */    ngx_errlog_commands,                   /* module directives */    NGX_CORE_MODULE,                       /* module type */    NULL,                                  /* init master */    NULL,                                  /* init module */    NULL,                                  /* init process */    NULL,                                  /* init thread */    NULL,                                  /* exit thread */    NULL,                                  /* exit process */    NULL,                                  /* exit master */    NGX_MODULE_V1_PADDING};


其中ngx_error_log.c的代码如下【主要实现了按照配置文件的命令创建新的日志文件和设置日志输出的等级】

char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log){    ngx_uint_t   i, n, d, found;    ngx_str_t   *value;    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;            }        }          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) {  //若已设置err_levels,则不允许设置debug level。  //即error_log  logs/error.log  DEBUG NGX_LOG_DEBUG;将报错。                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;            }        }        if (!found) {            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,                               "invalid log level \"%V\"", &value[i]);            return NGX_CONF_ERROR;        }    }    // 如果error_log  logs/error.log NGX_LOG_DEBUG;相当于设置了全部的debug level.    if (log->log_level == NGX_LOG_DEBUG) {        log->log_level = NGX_LOG_DEBUG_ALL;    }    return NGX_CONF_OK;}static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ngx_str_t  *value, name;    if (cf->cycle->new_log.file) {        return "is duplicate";    }    value = cf->args->elts;    if (ngx_strcmp(value[1].data, "stderr") == 0) {        ngx_str_null(&name);    } else {        name = value[1];    }    cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name);    if (cf->cycle->new_log.file == NULL) {        return NULL;    }    if (cf->args->nelts == 2) {        cf->cycle->new_log.log_level = NGX_LOG_ERR;        return NGX_CONF_OK;    }    cf->cycle->new_log.log_level = 0;    return ngx_log_set_levels(cf, &cf->cycle->new_log);}

若配置文件中没有配置使用error_log,则【ngx_cycle.c】如下代码,作用生成新的new_log。

(此处发现ngx_cycle_s中的new_log仅仅是为了记录新日志文件之用;另外为了记录日志初始化之前的日志,所以ngx_log_init中创建/usr/local/logs/error.log,若配置文件中重新配置,则以配置文件为准;若没有配置,则就以此文件记录日志

    if (cycle->new_log.file == NULL) {        cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);        if (cycle->new_log.file == NULL) {            goto failed;        }    }

最后,

    cycle->log = &cycle->new_log;    pool->log = &cycle->new_log;

至此日志可正常使用。