Nginx的变量系统是其配置灵活性的基石之一,允许用户在配置文件中引用动态值(如 <math xmlns="http://www.w3.org/1998/Math/MathML"> r e m o t e a d d r 、 remote_addr、 </math>remoteaddr、request_uri等),并在请求处理过程中进行赋值或读取。本文基于Nginx 1.22.1的源码,深入分析变量的注册、存储、获取机制,并结合请求的最终化过程,探讨变量在日志记录阶段的应用。我们将重点关注提供的代码片段,揭示Nginx内部如何管理变量,以及如何在日志处理中高效地获取变量值。
1. 变量的注册与存储
在Nginx中,变量分为两类:普通变量和前缀变量(prefix variables)。前者通过哈希表快速查找,后者适用于名称以特定前缀开头的变量(如$upstream_http_系列变量)。注册变量的核心函数是ngx_http_add_variable,它根据传入的flags决定是否调用前缀变量专用函数。
1.1 普通变量的添加
c
arduino
ngx_http_variable_t *
ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
// ... 省略错误检查 ...
if (flags & NGX_HTTP_VAR_PREFIX) {
return ngx_http_add_prefix_variable(cf, name, flags);
}
// 否则作为普通变量处理
}
普通变量被存储在ngx_http_core_main_conf_t结构体的variables_keys哈希键数组中。在添加之前,会遍历variables_keys->keys检查是否已存在同名变量。如果变量已存在且不可变(未设置NGX_HTTP_VAR_CHANGEABLE),则报错退出;若允许变更,则根据新变量的NGX_HTTP_VAR_WEAK标志调整原变量的弱属性。这种设计确保了变量定义的一致性,同时允许模块覆盖某些内置变量的行为(如弱变量允许在子请求中修改)。
新变量分配内存后,名称会通过ngx_strlow转换为小写存储(Nginx变量名不区分大小写)。然后调用ngx_hash_add_key将变量加入variables_keys哈希表,准备后续构建快速查找的哈希。
1.2 前缀变量的特殊处理
前缀变量用于处理那些无法枚举的变量名,例如从上游响应头部动态生成的变量。它们不参与哈希查找,而是存储在cmcf->prefix_variables动态数组中。ngx_http_add_prefix_variable同样检查重复,但只针对数组中的元素。如果已存在同名变量且不可变,则报错;否则更新弱标志。新增时,名称同样转换为小写存入内存。
前缀变量在请求处理中通过遍历数组进行匹配,虽然性能不如哈希直接,但其灵活性使得Nginx能够支持任意自定义变量名(如$upstream_http_系列)。
1.3 核心变量的初始化
在配置解析的早期阶段,ngx_http_core_preconfiguration函数被调用,它通过ngx_http_variables_add_core_vars将核心模块定义的内置变量(ngx_http_core_variables数组)注册到系统中。这些变量包括$uri、$request_uri、$args等,它们被逐个添加到variables_keys或prefix_variables中,并设置了对应的get_handler和set_handler。
c
ini
for (cv = ngx_http_core_variables; cv->name.len; cv++) {
v = ngx_http_add_variable(cf, &cv->name, cv->flags);
if (v == NULL) {
return NGX_ERROR;
}
*v = *cv;
}
注意,这里将ngx_http_core_variables中预定义的函数指针赋值给了新创建的变量,使得这些变量在后续被获取时能够执行特定的逻辑。
2. 请求处理与变量获取
变量最终在请求处理过程中被读取,最常见的场景之一是访问日志记录。当一个请求结束时,Nginx会执行ngx_http_finalize_request,其中可能触发日志记录。
2.1 请求的最终化
ngx_http_finalize_request是一个庞大的函数,负责根据返回码(rc)决定请求的后续动作。关键分支包括:
- 当
rc == NGX_DECLINED时,重置内容处理器并继续执行阶段; - 当发生错误或特殊响应时,可能调用
ngx_http_terminate_request; - 正常结束时,若为主请求且无缓冲数据,则调用
ngx_http_finalize_connection关闭连接。
在主请求正常结束前,会检查是否启用了post_action,否则标记请求完成。其中,子请求的处理涉及ngx_http_log_request的调用:
c
scss
if (r != r->main) {
// ... 子请求处理 ...
if (!r->logged) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->log_subrequest) {
ngx_http_log_request(r);
}
r->logged = 1;
}
// ...
}
无论是主请求还是子请求,最终都会通过ngx_http_log_request调用日志模块的handler,而日志模块的handler正是ngx_http_log_handler。
2.2 日志处理中的变量展开
ngx_http_log_handler负责将日志格式中的变量展开为实际字符串,并写入文件或syslog。其核心流程如下:
-
获取日志模块的配置
lcf,若日志关闭则直接返回。 -
遍历所有日志条目(可能多个日志文件或syslog),对每条日志:
- 若定义了filter,则计算过滤条件,跳过不满足的请求。
- 计算日志行的总长度:遍历格式操作符数组
ops,累加固定长度和变量长度(通过getlen回调)。 - 若使用缓冲(buffer),尝试将日志写入缓冲区,否则分配临时内存。
- 通过
run回调将变量值写入缓冲区。
其中,变量长度的计算由ngx_http_log_variable_getlen完成,它通过索引获取变量值,并返回转义后的长度。这个索引(data参数)是在配置解析阶段,根据变量名在variables数组中的位置确定的。
c
ini
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
return 1;
}
len = ngx_http_log_escape(NULL, value->data, value->len);
value->escape = len ? 1 : 0;
return value->len + len * 3;
2.3 获取索引变量的内部实现
ngx_http_get_indexed_variable是变量获取的核心函数,它通过索引快速访问请求的变量值缓存(r->variables数组),避免重复计算。其逻辑:
- 检查索引是否超出
cmcf->variables范围。 - 如果请求变量缓存有效或未找到,直接返回缓存值。
- 否则调用变量的
get_handler计算值,并存入缓存。同时使用ngx_http_variable_depth计数器防止递归循环(如变量A依赖变量B,变量B又依赖A)。
c
ini
if (ngx_http_variable_depth == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cycle while evaluating variable "%V"",
&v[index].name);
return NULL;
}
ngx_http_variable_depth--;
if (v[index].get_handler(r, &r->variables[index], v[index].data) == NGX_OK) {
ngx_http_variable_depth++;
// 设置nocacheable标志等
}
这种设计既保证了性能(缓存),又通过深度限制保护了递归解析。
2.4 动态未知头部的变量处理
对于$http_和sent_http_开头的变量,Nginx提供了通用处理函数ngx_http_variable_unknown_header。它遍历请求头或响应头链表,将变量名中的下划线转换为连字符(或反之),进行不区分大小写的匹配。例如,变量$http_user_agent会匹配请求头中的User-Agent。
c
ini
for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'A' && ch <= 'Z') {
ch |= 0x20;
} else if (ch == '-') {
ch = '_';
}
if (var->data[n + prefix] != ch) {
break;
}
}
这种动态匹配机制使得Nginx能够处理任意客户端发送的头部,无需预先定义所有变量。
3. 变量系统的设计巧思
从上述代码中,我们可以提炼出Nginx变量系统的几个关键设计:
- 两阶段注册:普通变量通过哈希表快速访问,前缀变量通过数组遍历,兼顾了效率与灵活性。
- 不可变性与弱变量 :
NGX_HTTP_VAR_CHANGEABLE标志控制变量是否允许被多次定义,防止模块间意外覆盖;NGX_HTTP_VAR_WEAK标志允许子请求修改变量值,但不影响主请求,这为复杂请求处理提供了便利。 - 缓存与深度保护 :每个请求维护一份变量值缓存,避免重复计算;同时通过
ngx_http_variable_depth防止循环依赖,体现了对异常情况的防御。 - 统一的接口 :变量通过
get_handler和set_handler函数指针提供操作,使得自定义变量可以轻松集成,如核心变量、第三方模块变量等。
4. 结语
通过分析Nginx变量系统的注册、存储与获取流程,我们看到了一个高性能、可扩展的设计典范。变量不仅在配置解析阶段被定义,更在请求处理的全生命周期中扮演着信息载体的角色。而日志记录作为变量最常见的消费场景,展示了Nginx如何高效地将变量展开为字符串。理解这些底层机制,有助于我们编写更高效的Nginx模块,也能更深刻地理解Nginx的内部运作原理。\r\n
源码
ini
static ngx_http_variable_t *
ngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
ngx_uint_t i;
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
v = cmcf->prefix_variables.elts;
for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
if (name->len != v[i].name.len
|| ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
{
continue;
}
v = &v[i];
if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the duplicate \"%V\" variable", name);
return NULL;
}
if (!(flags & NGX_HTTP_VAR_WEAK)) {
v->flags &= ~NGX_HTTP_VAR_WEAK;
}
return v;
}
v = ngx_array_push(&cmcf->prefix_variables);
if (v == NULL) {
return NULL;
}
v->name.len = name->len;
v->name.data = ngx_pnalloc(cf->pool, name->len);
if (v->name.data == NULL) {
return NULL;
}
ngx_strlow(v->name.data, name->data, name->len);
v->set_handler = NULL;
v->get_handler = NULL;
v->data = 0;
v->flags = flags;
v->index = 0;
return v;
}
ngx_http_variable_t *
ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
ngx_int_t rc;
ngx_uint_t i;
ngx_hash_key_t *key;
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
if (name->len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"$\"");
return NULL;
}
if (flags & NGX_HTTP_VAR_PREFIX) {
return ngx_http_add_prefix_variable(cf, name, flags);
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
key = cmcf->variables_keys->keys.elts;
for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
if (name->len != key[i].key.len
|| ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
{
continue;
}
v = key[i].value;
if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the duplicate \"%V\" variable", name);
return NULL;
}
if (!(flags & NGX_HTTP_VAR_WEAK)) {
v->flags &= ~NGX_HTTP_VAR_WEAK;
}
return v;
}
v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
if (v == NULL) {
return NULL;
}
v->name.len = name->len;
v->name.data = ngx_pnalloc(cf->pool, name->len);
if (v->name.data == NULL) {
return NULL;
}
ngx_strlow(v->name.data, name->data, name->len);
v->set_handler = NULL;
v->get_handler = NULL;
v->data = 0;
v->flags = flags;
v->index = 0;
rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
if (rc == NGX_ERROR) {
return NULL;
}
if (rc == NGX_BUSY) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"conflicting variable name \"%V\"", name);
return NULL;
}
return v;
}
ngx_int_t
ngx_http_variables_add_core_vars(ngx_conf_t *cf)
{
ngx_http_variable_t *cv, *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
sizeof(ngx_hash_keys_arrays_t));
if (cmcf->variables_keys == NULL) {
return NGX_ERROR;
}
cmcf->variables_keys->pool = cf->pool;
cmcf->variables_keys->temp_pool = cf->pool;
if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,
sizeof(ngx_http_variable_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (cv = ngx_http_core_variables; cv->name.len; cv++) {
v = ngx_http_add_variable(cf, &cv->name, cv->flags);
if (v == NULL) {
return NGX_ERROR;
}
*v = *cv;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_core_preconfiguration(ngx_conf_t *cf)
{
return ngx_http_variables_add_core_vars(cf);
}
void
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_connection_t *c;
ngx_http_request_t *pr;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http finalize request: %i, \"%V?%V\" a:%d, c:%d",
rc, &r->uri, &r->args, r == c->data, r->main->count);
if (rc == NGX_DONE) {
ngx_http_finalize_connection(r);
return;
}
if (rc == NGX_OK && r->filter_finalize) {
c->error = 1;
}
if (rc == NGX_DECLINED) {
r->content_handler = NULL;
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
return;
}
if (r != r->main && r->post_subrequest) {
rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
}
if (rc == NGX_ERROR
|| rc == NGX_HTTP_REQUEST_TIME_OUT
|| rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
|| c->error)
{
if (ngx_http_post_action(r) == NGX_OK) {
return;
}
ngx_http_terminate_request(r, rc);
return;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE
|| rc == NGX_HTTP_CREATED
|| rc == NGX_HTTP_NO_CONTENT)
{
if (rc == NGX_HTTP_CLOSE) {
c->timedout = 1;
ngx_http_terminate_request(r, rc);
return;
}
if (r == r->main) {
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
}
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
return;
}
if (r != r->main) {
if (r->buffered || r->postponed) {
if (ngx_http_set_write_handler(r) != NGX_OK) {
ngx_http_terminate_request(r, 0);
}
return;
}
pr = r->parent;
if (r == c->data || r->background) {
if (!r->logged) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->log_subrequest) {
ngx_http_log_request(r);
}
r->logged = 1;
} else {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"subrequest: \"%V?%V\" logged again",
&r->uri, &r->args);
}
r->done = 1;
if (r->background) {
ngx_http_finalize_connection(r);
return;
}
r->main->count--;
if (pr->postponed && pr->postponed->request == r) {
pr->postponed = pr->postponed->next;
}
c->data = pr;
} else {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http finalize non-active request: \"%V?%V\"",
&r->uri, &r->args);
r->write_event_handler = ngx_http_request_finalizer;
if (r->waited) {
r->done = 1;
}
}
if (ngx_http_post_request(pr, NULL) != NGX_OK) {
r->main->count++;
ngx_http_terminate_request(r, 0);
return;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http wake parent request: \"%V?%V\"",
&pr->uri, &pr->args);
return;
}
if (r->buffered || c->buffered || r->postponed) {
if (ngx_http_set_write_handler(r) != NGX_OK) {
ngx_http_terminate_request(r, 0);
}
return;
}
if (r != c->data) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"http finalize non-active request: \"%V?%V\"",
&r->uri, &r->args);
return;
}
r->done = 1;
r->read_event_handler = ngx_http_block_reading;
r->write_event_handler = ngx_http_request_empty_handler;
if (!r->post_action) {
r->request_complete = 1;
}
if (ngx_http_post_action(r) == NGX_OK) {
return;
}
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
c->write->delayed = 0;
ngx_del_timer(c->write);
}
ngx_http_finalize_connection(r);
}
static void
ngx_http_log_request(ngx_http_request_t *r)
{
ngx_uint_t i, n;
ngx_http_handler_pt *log_handler;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts; //在ngx_http_log_init函数把ngx_http_log_handler函数添加到handlers
for (i = 0; i < n; i++) {
log_handler[i](r);
}
}
static ngx_int_t
ngx_http_log_handler(ngx_http_request_t *r)
{
u_char *line, *p;
size_t len, size;
ssize_t n;
ngx_str_t val;
ngx_uint_t i, l;
ngx_http_log_t *log;
ngx_http_log_op_t *op;
ngx_http_log_buf_t *buffer;
ngx_http_log_loc_conf_t *lcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http log handler");
lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
if (lcf->off) {
return NGX_OK;
}
log = lcf->logs->elts;
for (l = 0; l < lcf->logs->nelts; l++) {
if (log[l].filter) {
if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {
return NGX_ERROR;
}
if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
continue;
}
}
if (ngx_time() == log[l].disk_full_time) {
/*
* on FreeBSD writing to a full filesystem with enabled softupdates
* may block process for much longer time than writing to non-full
* filesystem, so we skip writing to a log for one second
*/
continue;
}
ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);
len = 0;
op = log[l].format->ops->elts;
for (i = 0; i < log[l].format->ops->nelts; i++) {
if (op[i].len == 0) {
len += op[i].getlen(r, op[i].data);
} else {
len += op[i].len;
}
}
if (log[l].syslog_peer) {
/* length of syslog's PRI and HEADER message parts */
len += sizeof("<255>Jan 01 00:00:00 ") - 1
+ ngx_cycle->hostname.len + 1
+ log[l].syslog_peer->tag.len + 2;
goto alloc_line;
}
len += NGX_LINEFEED_SIZE;
buffer = log[l].file ? log[l].file->data : NULL;
if (buffer) {
if (len > (size_t) (buffer->last - buffer->pos)) {
ngx_http_log_write(r, &log[l], buffer->start,
buffer->pos - buffer->start);
buffer->pos = buffer->start;
}
if (len <= (size_t) (buffer->last - buffer->pos)) {
p = buffer->pos;
if (buffer->event && p == buffer->start) {
ngx_add_timer(buffer->event, buffer->flush);
}
for (i = 0; i < log[l].format->ops->nelts; i++) {
p = op[i].run(r, p, &op[i]);
}
ngx_linefeed(p);
buffer->pos = p;
continue;
}
if (buffer->event && buffer->event->timer_set) {
ngx_del_timer(buffer->event);
}
}
alloc_line:
line = ngx_pnalloc(r->pool, len);
if (line == NULL) {
return NGX_ERROR;
}
p = line;
if (log[l].syslog_peer) {
p = ngx_syslog_add_header(log[l].syslog_peer, line);
}
for (i = 0; i < log[l].format->ops->nelts; i++) {
p = op[i].run(r, p, &op[i]);
}
if (log[l].syslog_peer) {
size = p - line;
n = ngx_syslog_send(log[l].syslog_peer, line, size);
if (n < 0) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"send() to syslog failed");
} else if ((size_t) n != size) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"send() to syslog has written only %z of %uz",
n, size);
}
continue;
}
ngx_linefeed(p);
ngx_http_log_write(r, &log[l], line, p - line);
}
return NGX_OK;
}
static size_t
ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
{
uintptr_t len;
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
return 1;
}
len = ngx_http_log_escape(NULL, value->data, value->len);
value->escape = len ? 1 : 0;
return value->len + len * 3;
}
ngx_http_variable_value_t *
ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
{
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
if (cmcf->variables.nelts <= index) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"unknown variable index: %ui", index);
return NULL;
}
if (r->variables[index].not_found || r->variables[index].valid) {
return &r->variables[index];
}
v = cmcf->variables.elts;
if (ngx_http_variable_depth == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cycle while evaluating variable \"%V\"",
&v[index].name);
return NULL;
}
ngx_http_variable_depth--;
if (v[index].get_handler(r, &r->variables[index], v[index].data)
== NGX_OK)
{
ngx_http_variable_depth++;
if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
r->variables[index].no_cacheable = 1;
}
return &r->variables[index];
}
ngx_http_variable_depth++;
r->variables[index].valid = 0;
r->variables[index].not_found = 1;
return NULL;
}
static ngx_int_t
ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->headers_out.headers.part,
sizeof("sent_http_") - 1);
}
ngx_int_t
ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
ngx_list_part_t *part, size_t prefix)
{
u_char ch;
ngx_uint_t i, n;
ngx_table_elt_t *header;
header = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'A' && ch <= 'Z') {
ch |= 0x20;
} else if (ch == '-') {
ch = '_';
}
if (var->data[n + prefix] != ch) {
break;
}
}
if (n + prefix == var->len && n == header[i].key.len) {
v->len = header[i].value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = header[i].value.data;
return NGX_OK;
}
}
v->not_found = 1;
return NGX_OK;
}
//日志初始化模块
static ngx_int_t
ngx_http_log_init(ngx_conf_t *cf)
{
ngx_str_t *value;
ngx_array_t a;
ngx_http_handler_pt *h;
ngx_http_log_fmt_t *fmt;
ngx_http_log_main_conf_t *lmcf;
ngx_http_core_main_conf_t *cmcf;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
if (lmcf->combined_used) {
if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
return NGX_ERROR;
}
value = ngx_array_push(&a);
if (value == NULL) {
return NGX_ERROR;
}
*value = ngx_http_combined_fmt;
fmt = lmcf->formats.elts;
if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
!= NGX_CONF_OK)
{
return NGX_ERROR;
}
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); //日志阶段执行的处理函数列表 存储了所有注册到日志阶段的处理函数
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_log_handler;
return NGX_OK;
}