ngx_log_stderr 函数声明
ngx_core.h 中引入:
#include <ngx_log.h>
ngx_log_stderr 的声明在 ngx_log.h 中:
void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);
ngx_err_t 类型
在 ngx_errno.h 中
typedef int ngx_err_t;
其本质是 int 类型
ngx_core.h 中引入
#include <ngx_errno.h>
所以是通过 ngx_core.h 间接引入 ngx_err_t 类型
ngx_log_stderr 函数实现
ngx_log_stderr 函数的定义在 ngx_log.c 中:
void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...) { u_char *p, *last; va_list args; u_char errstr[NGX_MAX_ERROR_STR]; last = errstr + NGX_MAX_ERROR_STR; p = ngx_cpymem(errstr, "nginx: ", 7); va_start(args, fmt); p = ngx_vslprintf(p, last, fmt, args); va_end(args); if (err) { p = ngx_log_errno(p, last, err); } if (p > last - NGX_LINEFEED_SIZE) { p = last - NGX_LINEFEED_SIZE; } ngx_linefeed(p); (void) ngx_write_console(ngx_stderr, errstr, p - errstr); }
当 Nginx 运行时,可能会遇到各种各样的错误,比如文件找不到、网络连接失败等。
这段代码的作用是将这些错误信息以一种清晰、格式化的方式输出到标准错误输出(stderr)。
这样,开发者或运维人员可以通过查看这些错误信息,快速定位问题并进行修复。
这段代码是一个日志函数,专门用于处理错误日志。
它接收一个错误码、一个格式化字符串和可变参数,然后将这些信息组合成一条完整的错误日志,并输出到标准错误输出(stderr)。
它的主要目的是让错误信息更易于阅读和理解。
void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...)
void
:这个函数没有返回值,也就是说它只是执行一些操作,但不会返回任何结果
ngx_cdecl
:这是一个宏,用于指定函数的调用约定。调用约定是指函数参数如何传递、如何清理栈等细节。在这个上下文中,ngx_cdecl
确保函数按照标准的 C 调用约定执行。
ngx_log_stderr
:这是函数名,表示这是一个日志函数,专门用于将错误信息输出到标准错误输出(stderr)。
ngx_err_t err
:这是一个参数,类型是ngx_err_t
,表示错误码。错误码是一个数字,用来标识具体的错误类型。
const char *fmt
:这是一个格式化字符串,类似于printf
中的格式化字符串。它告诉函数如何将后面的参数格式化成字符串,用于指定错误信息的格式。
例如,
fmt
可能是"Failed to open file %s"
,其中%s
是一个占位符,表示后面会有一个字符串参数。
...
:这表示这是一个可变参数函数,可以接收不定数量的额外参数。这些参数会根据
fmt
的格式来解析。例如,如果
fmt
是"Failed to open file %s"
,那么后面可能会有一个参数是文件名,比如"config.txt"
。{ u_char *p, *last; va_list args; u_char errstr[NGX_MAX_ERROR_STR];
p
:指向缓冲区当前写入位置的指针。
last
:指向缓冲区末尾的指针(防溢出哨兵)。
args
:用于处理可变参数的va_list
类型。
errstr
:固定大小的缓冲区,存储最终输出的错误信息。
NGX_MAX_ERROR_STR
是一个宏,定义了这个数组的最大长度
NGX_MAX_ERROR_STR
定义在 ngx_log.h 中#define NGX_MAX_ERROR_STR 2048
C语言可变参数
last = errstr + NGX_MAX_ERROR_STR;
last
指向errstr
数组的末尾,用来标记数组的最大边界。即
errstr
数组的最后一个字符的下一个位置
p = ngx_cpymem(errstr, "nginx: ", 7);
ngx_cpymem:这是一个函数,用于将一段内存内容复制到另一段内存中。
这里将字符串
"nginx: "
复制到errstr
数组的开头,并返回复制后的新指针位置。
p
现在指向"nginx: "
后的第一个字符位置,用于后续继续写入错误信息。当我们执行这样的命令:
sudo ./nginx q
会得到这样的输出:nginx: invalid option: "q"
错误信息的格式就是以 nginx:
为开头的
ngx_cpymem 函数
在 ngx_log.c 的开头引入:
#include <ngx_core.h>
打开 ngx_core.h 可以找到:
#include <ngx_string.h>
打开 ngx_string.h 可以找到:
#if (NGX_MEMCPY_LIMIT) void *ngx_memcpy(void *dst, const void *src, size_t n); #define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n)) #else /* * gcc3, msvc, and icc7 compile memcpy() to the inline "rep movs". * gcc3 compiles memcpy(d, s, 4) to the inline "mov"es. * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves. */ #define ngx_memcpy(dst, src, n) (void) memcpy(dst, src, n) #define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n)) #endif
这里根据 NGX_MEMCPY_LIMIT 这个宏的定义来决定 ngx_cpymem 函数的声明具体是哪一个
那么我们来查找一下 NGX_MEMCPY_LIMIT 这个宏的定义吧
我们没有找到 NGX_MEMCPY_LIMIT 的定义
所以这里的
#if (NGX_MEMCPY_LIMIT)
条件不成立
因此 #else 后的内容成立
所以 ngx_cpymem 的声明是
#define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n))
memcpy(dst, src, n)
会从src
开始,复制n
个字节的数据到dst
在
memcpy
返回值的基础上加上n
,返回一个指向复制后数据末尾的指针
va_start(args, fmt);
va_start
是一个宏,用来初始化args
,让它指向可变参数的第一个参数。这样我们就可以逐个访问这些参数了。
p = ngx_vslprintf(p, last, fmt, args);
ngx_vslprintf
是一个函数,它的作用是根据fmt
的格式,将可变参数args
格式化成字符串,并存储到p
指向的位置。
last
是字符串的最大边界,防止溢出。
ngx_vslprintf
函数
va_end(args);
va_end
是一个宏,用来结束对可变参数的访问,释放相关资源
if (err) { p = ngx_log_errno(p, last, err); }
如果
err
不为 0(即有错误发生),调用ngx_log_errno
函数。
ngx_log_errno
会将错误码err
转换为对应的错误信息,并追加到p
指向的位置
ngx_log_errno
函数
if (p > last - NGX_LINEFEED_SIZE) { p = last - NGX_LINEFEED_SIZE; }
检查
p
是否超出了errstr
的最大长度减去换行符的大小(NGX_LINEFEED_SIZE
)。如果超出,将
p
调整到最大边界减去换行符大小的位置,防止溢出。留出换行符的空间
NGX_LINEFEED_SIZE 宏
os\unix\ngx_files.h 中
#define NGX_LINEFEED_SIZE 1
在linux中换行符是 "\n" ,占一个字节
在windows中换行符是 "\r\n",占2个字节
ngx_linefeed(p);
ngx_linefeed
是一个函数,用来在p
指向的位置添加换行符。
ngx_linefeed
函数os\unix\ngx_files.h 中
#define ngx_linefeed(p) *p++ = LF;
(void) ngx_write_console(ngx_stderr, errstr, p - errstr);
ngx_write_console
是一个函数,用来将errstr
数组中的内容输出到标准错误输出(ngx_stderr
)。
p - errstr
是输出内容的长度。
ngx_write_console 函数
os\unix\ngx_files.h 中
/* * we use inlined function instead of simple #define * because glibc 2.3 sets warn_unused_result attribute for write() * and in this case gcc 4.3 ignores (void) cast */ static ngx_inline ssize_t ngx_write_fd(ngx_fd_t fd, void *buf, size_t n) { return write(fd, buf, n); } #define ngx_write_fd_n "write()" #define ngx_write_console ngx_write_fd
本质就是调用 write 函数
write
函数是C语言中用于执行低级文件写入操作的系统调用,直接通过文件描述符(File Descriptor)将数据写入文件或设备
ngx_stderr
os\unix\ngx_files.h 中
#define ngx_stderr STDERR_FILENO
STDERR_FILENO 是一个宏,它的值通常是数字
2
。它被用来表示标准错误输出的文件描述符需要引入 <unistd.h> 来使用