src\http\modules\ngx_http_log_module.c
static char *
ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
{
u_char *data, *p, ch;
size_t i, len;
ngx_str_t *value, var;
ngx_int_t *flush;
ngx_uint_t bracket, escape;
ngx_http_log_op_t *op;
ngx_http_log_var_t *v;
escape = NGX_HTTP_LOG_ESCAPE_DEFAULT;
value = args->elts;
if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
data = value[s].data + 7;
if (ngx_strcmp(data, "json") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_JSON;
} else if (ngx_strcmp(data, "none") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_NONE;
} else if (ngx_strcmp(data, "default") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"不明なログ形式のエスケープ方式 \"%s\"", data);
return NGX_CONF_ERROR;
}
s++;
}
for ( /* void */ ; s < args->nelts; s++) {
i = 0;
while (i < value[s].len) {
op = ngx_array_push(ops);
if (op == NULL) {
return NGX_CONF_ERROR;
}
data = &value[s].data[i];
if (value[s].data[i] == '$') {
if (++i == value[s].len) {
goto invalid;
}
if (value[s].data[i] == '{') {
bracket = 1;
if (++i == value[s].len) {
goto invalid;
}
var.data = &value[s].data[i];
} else {
bracket = 0;
var.data = &value[s].data[i];
}
for (var.len = 0; i < value[s].len; i++, var.len++) {
ch = value[s].data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
}
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
if (bracket) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" 変数の閉じ括弧がありません", &var);
return NGX_CONF_ERROR;
}
if (var.len == 0) {
goto invalid;
}
for (v = ngx_http_log_vars; v->name.len; v++) {
if (v->name.len == var.len
&& ngx_strncmp(v->name.data, var.data, var.len) == 0)
{
op->len = v->len;
op->getlen = NULL;
op->run = v->run;
op->data = 0;
goto found;
}
}
if (ngx_http_log_variable_compile(cf, op, &var, escape)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (flushes) {
flush = ngx_array_push(flushes);
if (flush == NULL) {
return NGX_CONF_ERROR;
}
*flush = op->data; /* 変数インデックス */
}
found:
continue;
}
i++;
while (i < value[s].len && value[s].data[i] != '$') {
i++;
}
len = &value[s].data[i] - data;
if (len) {
op->len = len;
op->getlen = NULL;
if (len <= sizeof(uintptr_t)) {
op->run = ngx_http_log_copy_short;
op->data = 0;
while (len--) {
op->data <<= 8;
op->data |= data[len];
}
} else {
op->run = ngx_http_log_copy_long;
p = ngx_pnalloc(cf->pool, len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
ngx_memcpy(p, data, len);
op->data = (uintptr_t) p;
}
}
}
}
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "無効なパラメータ \"%s\"", data);
return NGX_CONF_ERROR;
}
この関数はNginxのログ形式文字列をコンパイルするためのものです。log_formatディレクティブの引数を一連の操作命令に変換し、ログ記録時に効率的に実行できるようにします。
関数シグネチャ
static char *
ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
パラメータの説明:
cf: 設定構造体へのポインタ。設定関連情報とメモリプールを含みますflushes: フラッシュする変数インデックスを格納する配列ops: 生成された操作命令を格納する配列args: ログ形式のパラメータを含む配列s: 開始パラメータインデックス
変数宣言部分
u_char *data, *p, ch;
size_t i, len;
ngx_str_t *value, var;
ngx_int_t *flush;
ngx_uint_t bracket, escape;
ngx_http_log_op_t *op;
ngx_http_log_var_t *v;
data,p,ch: 文字列処理のための一時ポインタと文字i,len: ループインデックスと長さ変数value: パラメータ配列要素へのポインタvar: 解析された変数名を格納flush: flushes配列を操作するためのポインタbracket:${}構文を使用するかどうかのフラグescape: エスケープ方式のフラグop: 現在の操作命令へのポインタv: 事前定義された変数を走査するためのポインタ
初期化部分
escape = NGX_HTTP_LOG_ESCAPE_DEFAULT;
value = args->elts;
- デフォルトのエスケープ方式を
NGX_HTTP_LOG_ESCAPE_DEFAULTに設定 - パラメータ配列の要素ポインタを取得
escapeパラメータの処理
if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
data = value[s].data + 7;
if (ngx_strcmp(data, "json") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_JSON;
} else if (ngx_strcmp(data, "none") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_NONE;
} else if (ngx_strcmp(data, "default") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"不明なログ形式のエスケープ方式 \"%s\"", data);
return NGX_CONF_ERROR;
}
s++;
}
escape=パラメータがあるかチェック- エスケープ方式を解析して設定:
json、none、またはdefault - 不明なエスケープ方式の場合はエラーを返す
- パラメータインデックス
sを増加
各パラメータを処理するメインループ
for ( /* void */ ; s < args->nelts; s++) {
i = 0;
while (i < value[s].len) {
- インデックス
sからすべてのパラメータを走査 - 各パラメータの位置0から文字ごとに処理
変数処理部分
op = ngx_array_push(ops);
if (op == NULL) {
return NGX_CONF_ERROR;
}
data = &value[s].data[i];
if (value[s].data[i] == '$') {
- ops配列に新しい操作用の領域を割り当て
- 割り当てが成功したかチェック
- 現在位置のポインタを取得
$記号が見つかった場合、変数処理を開始
変数名の処理
if (++i == value[s].len) {
goto invalid;
}
if (value[s].data[i] == '{') {
bracket = 1;
if (++i == value[s].len) {
goto invalid;
}
var.data = &value[s].data[i];
} else {
bracket = 0;
var.data = &value[s].data[i];
}
$の次の文字に進み、範囲チェック{が続く場合は${}構文として扱い、対応するフラグを設定- それ以外の場合は通常の変数構文として扱う
変数名の抽出
for (var.len = 0; i < value[s].len; i++, var.len++) {
ch = value[s].data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
}
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
- 変数名の長さを0から始めてカウント
}が見つかって${}構文の場合はループを終了- 変数名として有効な文字(英数字、アンダースコア)かチェック
- 無効な文字が見つかったらループを終了
変数名の検証
if (bracket) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" 変数の閉じ括弧がありません", &var);
return NGX_CONF_ERROR;
}
if (var.len == 0) {
goto invalid;
}
${構文で対応する}がない場合エラー- 変数名の長さが0の場合エラー
事前定義された変数の検索
for (v = ngx_http_log_vars; v->name.len; v++) {
if (v->name.len == var.len
&& ngx_strncmp(v->name.data, var.data, var.len) == 0)
{
op->len = v->len;
op->getlen = NULL;
op->run = v->run;
op->data = 0;
goto found;
}
}
ngx_http_log_varsテーブルを走査- 一致する変数が見つかったら操作命令を設定して
foundにジャンプ
変数のコンパイル
if (ngx_http_log_variable_compile(cf, op, &var, escape) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (flushes) {
flush = ngx_array_push(flushes);
if (flush == NULL) {
return NGX_CONF_ERROR;
}
*flush = op->data; /* 変数インデックス */
}
- 事前定義されていない変数の場合はコンパイル
- flushesが必要な場合は変数インデックスを追加
見つかった変数の処理
found:
continue;
- 次のループ反復に進む
通常テキストの処理
i++;
while (i < value[s].len && value[s].data[i] != '$') {
i++;
}
len = &value[s].data[i] - data;
if (len) {
op->len = len;
op->getlen = NULL;
if (len <= sizeof(uintptr_t)) {
op->run = ngx_http_log_copy_short;
op->data = 0;
while (len--) {
op->data <<= 8;
op->data |= data[len];
}
} else {
op->run = ngx_http_log_copy_long;
p = ngx_pnalloc(cf->pool, len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
ngx_memcpy(p, data, len);
op->data = (uintptr_t) p;
}
}
$ではない通常テキストの処理(2つの$の間の内容)- テキストの長さを計算
- テキストが存在する場合、操作命令を設定
- 短いテキストは直接
op->dataに格納 - 長いテキストはメモリを確保してポインタを格納
戻り値とエラー処理
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "無効なパラメータ \"%s\"", data);
return NGX_CONF_ERROR;
- 成功時は
NGX_CONF_OKを返す - エラー時はログを出力して
NGX_CONF_ERRORを返す