NGX_HTTP_LOG_PHASE挂载了ngx_http_log_handler函数
当一个r执行到当前log_phase阶段时,会调用该函数执行log打印
首先看下ngx_http_log_module模块
log_format命令 (NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE)
栗子:
log_format main '$request_uri$args$remote_addr - $remote_user [$time_local] "$request"$args ';
把设置的format都保存在lmcf->formats.elts里面,elts是一块类型为ngx_http_log_fmt_t的空间
其中fmt->name对于文中的栗子,即设置为main
对fmt的flushes申请空间,类型为ngx_int_t
对fmt的ops分配空间,类型为ngx_http_log_op_t
接下来调用ngx_http_log_compile_format函数
调用形式为:ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2)
第一个参数cf为当前阶段的cf
第二个参数fmt->flushes,第三个参数fmt->ops都为上文分配的空间
第四个参数cf->args为读取到的配置文件的内容
第五个参数设置为2,根据函数的执行,其实就是设置了对cf->args从第几个开始分析
该函数执行流程为:
遍历log格式的每一个字段,一个字段对应一个 ngx_http_log_op_s类型的存储空间。
首先根据一些特殊的标记来找出一个完整的变量,
遍历ngx_http_log_vars,查看当前查找到的变量是否在log_vars中,
如果在,把当前变量对应的op做如下赋值
op->len = v->len;
op->getlen = NULL;
op->run = v->run;
op->data = 0;
其中v为log模块的ngx_http_log_vars中的一个变量
然后接着找下一个字段。
如果不在,调用ngx_http_log_variable_compile
调用形式为:ngx_http_log_variable_compile(cf, op, &var)
第一个参数为main的cf
op为当前找到的字段对应的op,
var为当前字段
该函数执行流程为:
首先获取到当前字段的index,
对当前op进行如下赋值:
op->len = 0;
op->getlen = ngx_http_log_variable_getlen;
op->run = ngx_http_log_variable;
op->data = index
回到主函数,如果传递下来的flush不为空,则把当前字段的index存储到flush中
再往下走,是处理不是变量,也就是纯字符串的情况,纯字符的结束就两种情况,格式化字符串结束或者遇到$
找到完整的纯字符后,则对op赋值
这里有两种情况,如果是比较短的复制,则会直接把数值存放到op->data中,其op->run指向ngx_http_log_copy_short
如果是比较长的复制,则会把当前纯字符申请一块空间,然后让op->data指向该空间,其op->run指向ngx_http_log_copy_long
总结下就是:该函数会把所有的log格式字段对应的操作存放到lmcf->formats->ops 和lmcf->formats->flushes中
接下来再分析access_log命令
作用范围:NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_HTTP_LMT_CONF|NGX_CONF_1MORE
栗子 access_log logs/access.log main;
函数调用形式为:ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
第一个参数为各个阶段传递下来的cf,不同阶段的cf,主要的不同是cf->ctx
如果是access_log off,则会 把llcf->off赋值为1,应该代表此location不会打印log,还需证实?
接下来对llcf->logs分配空间,类型为ngx_http_log_t
如果value[1]为syslog:
稍后分析
查看value[1]是否包含变量,
如果不包含变量,则直接打开该文件 log->file = ngx_conf_open_file(cf->cycle, &value[1]);
如果包含变量,首先调用ngx_conf_full_name函数,
该函数主要调用方式为:ngx_conf_full_name(cf->cycle, &value[1], 0)
第一个参数为当前阶段的cf
第二个参数为access_log后面的字段
第三个参数为0
获取cf->cycle里面的prefix(等同于configure里面的--prefix的值)
调用子函数ngx_get_full_name
调用方式为:ngx_get_full_name(cycle->pool, prefix, name)
第三个参数name即为access_log的value[1]
该函数首先调用ngx_test_full_name(name),对于linux主要就是查看第一个字符是不是"/"
如果是直接返回,如果不是,则会重新分配空间,加上前缀,组成一个完整路径,然后修改name的变量,data指向重新分配的空间。
接下来对于log->script分开空间,调用ngx_http_script_compile,其执行过程和变量执行过程一样
接下来处理value[2],也就是log的格式
如果没有log格式,则默认使用combined,如果就是设置的combined,则会把combined_used设置为1
在lmcf->formats.elts里面寻找当前log使用的log格式,并把log->format指向寻找到的fmt
接下来处理value[2]以后的配置项
buffer值储存到size里面
flush值储存到flush里面
gzip值储存到gzip里面
有无if(if暂时不看)
open_log_file_cache命令,主要针对的是log文件名中有变量的情况
在程序运行的时候首先会调用ngx_http_log_init函数,调用方式如下:ngx_http_log_init(ngx_conf_t *cf),其中cf为http阶段的cf
函数的执行流程为:
如果使用的是combined格式的log,会调用ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)函数,该函数的解释参看上面log_format命令的解释,把ngx_http_log_handler函数挂载到NGX_HTTP_LOG_PHASE阶段
ngx_http_log_handler函数调用方式为:ngx_http_log_handler(ngx_http_request_t *r)
执行流程为:
检查lcf->off,不论打印几种log,只要任何一个设置了access_log off,则会关闭所有的log打印。
接下来遍历lcf->logs,(access_log指令添加)
判断当前log有无if设置,也就是filter设置。
调用ngx_http_script_flush_no_cacheable_variables函数,
调用方式ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);对log[l].format->flushes里面的变量的标志位重新设置,获取到最新的值
获取所有字段拼接的字符串的长度
如果配置了buffer,当前buffer有足够空间,调用ngx_http_log_write函数写,目前看到对于写日志没有做异常处理,会有漏日志的可能。
如果没有配置buffer,则调用每个ops的run函数,也就是每个变量的run函数,并且保存变量的值,打印到log。
ngx_linefeed就是在每条log后面加换行。