core/ngx_log.h头文件分析
本节我们主要讲述一下nginx中日志的相关实现。
1. 相关宏定义
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */
#ifndef _NGX_LOG_H_INCLUDED_
#define _NGX_LOG_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_LOG_STDERR            0
#define NGX_LOG_EMERG             1
#define NGX_LOG_ALERT             2
#define NGX_LOG_CRIT              3
#define NGX_LOG_ERR               4
#define NGX_LOG_WARN              5
#define NGX_LOG_NOTICE            6
#define NGX_LOG_INFO              7
#define NGX_LOG_DEBUG             8
#define NGX_LOG_DEBUG_CORE        0x010
#define NGX_LOG_DEBUG_ALLOC       0x020
#define NGX_LOG_DEBUG_MUTEX       0x040
#define NGX_LOG_DEBUG_EVENT       0x080
#define NGX_LOG_DEBUG_HTTP        0x100
#define NGX_LOG_DEBUG_MAIL        0x200
#define NGX_LOG_DEBUG_STREAM      0x400
/*
 * do not forget to update debug_levels[] in src/core/ngx_log.c
 * after the adding a new debug level
 */
#define NGX_LOG_DEBUG_FIRST       NGX_LOG_DEBUG_CORE
#define NGX_LOG_DEBUG_LAST        NGX_LOG_DEBUG_STREAM
#define NGX_LOG_DEBUG_CONNECTION  0x80000000
#define NGX_LOG_DEBUG_ALL         0x7ffffff0上面宏定义可以分成3个部分:
1) 定义日志的打印级别
#define NGX_LOG_STDERR 0 #define NGX_LOG_EMERG 1 #define NGX_LOG_ALERT 2 #define NGX_LOG_CRIT 3 #define NGX_LOG_ERR 4 #define NGX_LOG_WARN 5 #define NGX_LOG_NOTICE 6 #define NGX_LOG_INFO 7 #define NGX_LOG_DEBUG 8
对于每一个logger,只有那些高于当前所设置的打印级别的信息才会打印出来。
2) 指示debug模块
#define NGX_LOG_DEBUG_CORE 0x010 #define NGX_LOG_DEBUG_ALLOC 0x020 #define NGX_LOG_DEBUG_MUTEX 0x040 #define NGX_LOG_DEBUG_EVENT 0x080 #define NGX_LOG_DEBUG_HTTP 0x100 #define NGX_LOG_DEBUG_MAIL 0x200 #define NGX_LOG_DEBUG_STREAM 0x400
对于debug级别的日志,在进行日志打印时还会检查相应的掩码。
这里注意: 
1) 上面NGX_LOG_STDERR~NGX_LOG_DEBUG表示的是日志打印级别中的大级别,用4个bit位即可表示。
    并且日志级别只能属于上述大级别中的一个。
2) 而NGX_LOG_DEBUG_CORE~NGX_LOG_DEBUG_STREAM,是debug级别中的小级别,可以同时存在多个小
    级别,这样需要7个bit位类表示
3) 如果设置为NGX_LOG_DEBUG这样一个debug大级别,则包含所有NGX_LOG_DEBUG_CORE~NGX_LOG_DEBUG_STREAM
   中的所有小级别,此时log_level会被直接设置为NGX_LOG_DEBUG_ALL3) debug相应掩码总结
- 
    NGX_LOG_DEBUG_FIRST: 第一个debug模块
- 
    ```NGX_LOG_DEBUG_LAST``: 最后一个debug模块 
- 
    NGX_LOG_DEBUG_CONNECTION: 用于指示nginx中相应连接的debug信息的掩码
- 
    NGX_LOG_DEBUG_ALL: nginx中所有debug信息的掩码
2. ngx_log_s数据结构
typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);
typedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level,
    u_char *buf, size_t len);
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_log_writer_pt    writer;
    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_s是对日志数据结构的抽象,下面详细介绍一下各字段:
- 
    log_level: 指示当前的日志打印级别
- 
    file: 指示当前日志对象所关联的文件
- 
    connection: 当前引用该日志对象的连接数(connection number)
- 
    disk_full_time: 指示日志硬盘满的时间
- 
    handler: 日志信息处理器(一般是对日志进行格式化)
- 
    data: 一般作为日志信息处理的一个上下文对象。例如: 当前要用handler格式化一个http request信息等,此时可以用data字段来存放该上下文。(可以通过查询log->data,找到相应的用法)
- 
    writer: 进行日志写操作处理器
- 
    wdata: 作为日志写操作处理器的一个上下文对象。例如: 当writer是写到内存中时,wdata可能关联的就是一个ngx_log_memory_buf_t对象。
- 
    action: 用于指示在写日志前,所执行的操作(比如当前正在进行SSL handshaking,或者正在closing request等)
- 
    next: 指向下一个日志对象
3. 日志打印核心函数声明
#define NGX_MAX_ERROR_STR   2048
/*********************************/
#if (NGX_HAVE_C99_VARIADIC_MACROS)
#define NGX_HAVE_VARIADIC_MACROS  1
#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 char *fmt, ...);
#define ngx_log_debug(level, log, ...)                                        \
    if ((log)->log_level & level)                                             \
        ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__)
/*********************************/
#elif (NGX_HAVE_GCC_VARIADIC_MACROS)
#define NGX_HAVE_VARIADIC_MACROS  1
#define ngx_log_error(level, log, args...)                                    \
    if ((log)->log_level >= level) ngx_log_error_core(level, log, args)
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
    const char *fmt, ...);
#define ngx_log_debug(level, log, args...)                                    \
    if ((log)->log_level & level)                                             \
        ngx_log_error_core(NGX_LOG_DEBUG, log, args)
/*********************************/
#else /* no variadic macros */
#define NGX_HAVE_VARIADIC_MACROS  0
void ngx_cdecl ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
    const char *fmt, ...);
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
    const char *fmt, va_list args);
void ngx_cdecl ngx_log_debug_core(ngx_log_t *log, ngx_err_t err,
    const char *fmt, ...);
#endif /* variadic macros */
/*********************************/这里首先定义了NGX_MAX_ERROR_STR为2048,即单条日志的最大长度为2048。
当前在objs/ngx_auto_config.h头文件中,我们有如下宏定义:
#ifndef NGX_HAVE_C99_VARIADIC_MACROS #define NGX_HAVE_C99_VARIADIC_MACROS 1 #endif #ifndef NGX_HAVE_GCC_VARIADIC_MACROS #define NGX_HAVE_GCC_VARIADIC_MACROS 1 #endif
我们当前同时支持C99可变参数宏定义与gcc可变参数宏定义。其中c99可变参数宏定义类似于如下:
#define var(dummy, ...) sprintf(__VA_ARGS__)
gcc可变参数宏定义类似于如下:
#define var(dummy, args...) sprintf(args)
上面我们定义了三个函数:
- 
    ngx_log_error(): 打印一般的错误日志消息(只有高于对应错误日志级别,才会打印出来) 
- 
    ngx_log_error_core(): 打印日志的核心函数 
- 
    ngx_log_debug_core(): 打印debug级别日志的函数 
4. ngx_log_debug辅助函数
#if (NGX_DEBUG)
#if (NGX_HAVE_VARIADIC_MACROS)
#define ngx_log_debug0(level, log, err, fmt)                                  \
        ngx_log_debug(level, log, err, fmt)
#define ngx_log_debug1(level, log, err, fmt, arg1)                            \
        ngx_log_debug(level, log, err, fmt, arg1)
#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \
        ngx_log_debug(level, log, err, fmt, arg1, arg2)
#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \
        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3)
#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \
        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4)
#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \
        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)
#define ngx_log_debug6(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6)                    \
        ngx_log_debug(level, log, err, fmt,                                   \
                       arg1, arg2, arg3, arg4, arg5, arg6)
#define ngx_log_debug7(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \
        ngx_log_debug(level, log, err, fmt,                                   \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)
#define ngx_log_debug8(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \
        ngx_log_debug(level, log, err, fmt,                                   \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
#else /* no variadic macros */
#define ngx_log_debug0(level, log, err, fmt)                                  \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt)
#define ngx_log_debug1(level, log, err, fmt, arg1)                            \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1)
#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1, arg2)
#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3)
#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4)
#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5)
#define ngx_log_debug6(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6)                    \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)
#define ngx_log_debug7(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt,                                     \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)
#define ngx_log_debug8(level, log, err, fmt,                                  \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \
    if ((log)->log_level & level)                                             \
        ngx_log_debug_core(log, err, fmt,                                     \
                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
#endif
#else /* !NGX_DEBUG */
#define ngx_log_debug0(level, log, err, fmt)
#define ngx_log_debug1(level, log, err, fmt, arg1)
#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)
#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)
#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)
#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)
#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)
#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \
                       arg6, arg7)
#define ngx_log_debug8(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \
                       arg6, arg7, arg8)
#endif如上这些函数只是为了使用方便,针对ngx_log_debug()可变参数函数的特定参数个数时,进行定制。
当前我们并未开启NGX_DEBUG,要想开启NGX_DEBUG宏,请在编译时添加--with-debug选项。
5. 相关函数声明
//1) nginx 日志初始化
ngx_log_t *ngx_log_init(u_char *prefix);
//2) 打印alert级别日志,表示出现严重错误
void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);
//3) 将相应的日志信息打印到标准输出
void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);
//4) 答应相关errno的日志(注意此函数至少保证留有50个字节的空间来打印errno日志)
u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);
//5) 打开默认日志文件
ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle);
//6) 重定向标准错误。即将标准错误输出重定向到某一个日志文件
ngx_int_t ngx_log_redirect_stderr(ngx_cycle_t *cycle);
//7) 获得一个文件日志对象
ngx_log_t *ngx_log_get_file_log(ngx_log_t *head);
//8) 根据ngx_conf_t设置日志对象
char *ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head);6. 相关静态函数定义
/*
 * ngx_write_stderr() cannot be implemented as macro, since
 * MSVC does not allow to use #ifdef inside macro parameters.
 *
 * ngx_write_fd() is used instead of ngx_write_console(), since
 * CharToOemBuff() inside ngx_write_console() cannot be used with
 * read only buffer as destination and CharToOemBuff() is not needed
 * for ngx_write_stderr() anyway.
 */
static ngx_inline void
ngx_write_stderr(char *text)
{
    (void) ngx_write_fd(ngx_stderr, text, ngx_strlen(text));
}
static ngx_inline void
ngx_write_stdout(char *text)
{
    (void) ngx_write_fd(ngx_stdout, text, ngx_strlen(text));
}上述两个函数分别用于将text输出到标准错误以及标准输出中。关于为什么ngx_write_stderr()不能采用宏来实现,上面注释已经很清楚,不过这主要是针对MSVC上的实现,即Windows版本nginx,对于linux版本并没有此问题。
而关于为什么使用ngx_write_fd()函数,上面的注释当前已经过于老旧。目前ngx_write_console()函数实现如下:
#define ngx_write_console ngx_write_fd
7. 相关变量声明
extern ngx_module_t  ngx_errlog_module;
extern ngx_uint_t    ngx_use_stderr;上面声明了两个变量:
- 
    ngx_errlog_module: errlog模块相应数据结构变量
- 
    ngx_use_stderr: 该变量主要用于控制是否输出到标准错误。一般在Nginx启动的时候,会将ngx_use_stderr设置为1,这时用户可以从标准错误中看到相应的启动输出信息,启动之后就会被设置为0,这时nginx以静默方式运行。
[参考]

