diff options
author | Zhidao HONG <z.hong@f5.com> | 2024-10-28 17:45:19 +0800 |
---|---|---|
committer | Zhidao HONG <z.hong@f5.com> | 2024-11-14 09:43:49 +0800 |
commit | bc838c5e1b6270c5195c4bdaa6a0a4bb72e48549 (patch) | |
tree | dc84b5da1946ca521cd78ac821640e7fae50f3c2 | |
parent | a760e24a2c4e5f93703bb365b12807d8b1035cf5 (diff) | |
download | unit-bc838c5e1b6270c5195c4bdaa6a0a4bb72e48549.tar.gz unit-bc838c5e1b6270c5195c4bdaa6a0a4bb72e48549.tar.bz2 |
http: Support JSON format in access log
Allow format to be an object to generate JSON logs. The object keys
become JSON field names, and values support string, variable, and JS.
Note that when there is no JS in the format values, the object will
be pre-serialized to a JSON template string at configuration phase
for better performance.
Example config:
{
"access_log": {
"path": "/tmp/access.log",
"format": {
"remote_addr": "$remote_addr",
"time_local": "$time_local",
"request_line": "$request_line",
"status": "$status",
"body_bytes_sent": "$body_bytes_sent",
"header_referer": "$header_referer",
"header_user_agent": "$header_user_agent"
}
}
}
-rw-r--r-- | src/nxt_conf_validation.c | 51 | ||||
-rw-r--r-- | src/nxt_router_access_log.c | 194 |
2 files changed, 229 insertions, 16 deletions
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 0dde39ff..a0b4992f 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -216,6 +216,11 @@ static nxt_int_t nxt_conf_vldt_server_weight(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_access_log(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_access_log_format(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_access_log_format_field( + nxt_conf_validation_t *vldt, const nxt_str_t *name, + nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -1465,7 +1470,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_access_log_members[] = { .type = NXT_CONF_VLDT_STRING, }, { .name = nxt_string("format"), - .type = NXT_CONF_VLDT_STRING, + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_access_log_format, }, { .name = nxt_string("if"), .type = NXT_CONF_VLDT_STRING, @@ -3624,3 +3630,46 @@ nxt_conf_vldt_access_log(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, return NXT_OK; } + + +static nxt_int_t +nxt_conf_vldt_access_log_format(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + static const nxt_str_t format = nxt_string("format"); + + if (nxt_conf_type(value) == NXT_CONF_OBJECT) { + return nxt_conf_vldt_object_iterator(vldt, value, + nxt_conf_vldt_access_log_format_field); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_access_log_format_field(vldt, &format, value); +} + + +static nxt_int_t +nxt_conf_vldt_access_log_format_field(nxt_conf_validation_t *vldt, + const nxt_str_t *name, nxt_conf_value_t *value) +{ + nxt_str_t str; + + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, "In the access log format, the name " + "must not be empty."); + } + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "In the access log format, the value " + "must be a string."); + } + + nxt_conf_get_string(value, &str); + + if (nxt_is_tstr(&str)) { + return nxt_conf_vldt_var(vldt, name, &str); + } + + return NXT_OK; +} diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c index 30b38aa1..1ec1a2d2 100644 --- a/src/nxt_router_access_log.c +++ b/src/nxt_router_access_log.c @@ -11,20 +11,28 @@ typedef struct { - nxt_str_t path; - nxt_conf_value_t *format; - nxt_conf_value_t *expr; + nxt_str_t path; + nxt_conf_value_t *format; + nxt_conf_value_t *expr; } nxt_router_access_log_conf_t; typedef struct { - nxt_str_t text; - nxt_router_access_log_t *access_log; + nxt_str_t text; + nxt_router_access_log_t *access_log; } nxt_router_access_log_ctx_t; +typedef struct { + nxt_str_t name; + nxt_tstr_t *tstr; +} nxt_router_access_log_member_t; + + struct nxt_router_access_log_format_s { - nxt_tstr_t *tstr; + nxt_tstr_t *tstr; + nxt_uint_t nmembers; + nxt_router_access_log_member_t *member; }; @@ -33,6 +41,11 @@ static nxt_router_access_log_format_t *nxt_router_access_log_format_create( static void nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, nxt_router_access_log_t *access_log, nxt_router_access_log_format_t *format); +static nxt_int_t nxt_router_access_log_text(nxt_task_t *task, + nxt_http_request_t *r, nxt_router_access_log_ctx_t *ctx, nxt_tstr_t *tstr); +static nxt_int_t nxt_router_access_log_json(nxt_task_t *task, + nxt_http_request_t *r, nxt_router_access_log_ctx_t *ctx, + nxt_router_access_log_format_t *format); static void nxt_router_access_log_write(nxt_task_t *task, nxt_http_request_t *r, nxt_router_access_log_ctx_t *ctx); static void nxt_router_access_log_ready(nxt_task_t *task, @@ -145,7 +158,12 @@ static nxt_router_access_log_format_t * nxt_router_access_log_format_create(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_value_t *value) { - nxt_str_t str; + size_t size; + uint32_t i, n, next; + nxt_str_t name, str, *dst; + nxt_bool_t has_js; + nxt_conf_value_t *cv; + nxt_router_access_log_member_t *member; nxt_router_access_log_format_t *format; static const nxt_str_t default_format = nxt_string("$remote_addr - - " @@ -159,7 +177,74 @@ nxt_router_access_log_format_create(nxt_task_t *task, nxt_router_conf_t *rtcf, } if (value != NULL) { - nxt_conf_get_string(value, &str); + + if (nxt_conf_type(value) == NXT_CONF_OBJECT) { + next = 0; + has_js = 0; + + n = nxt_conf_object_members_count(value); + + for ( ;; ) { + cv = nxt_conf_next_object_member(value, &name, &next); + if (cv == NULL) { + break; + } + + nxt_conf_get_string(cv, &str); + + if (nxt_tstr_is_js(&str)) { + has_js = 1; + } + } + + if (has_js) { + member = nxt_mp_alloc(rtcf->mem_pool, + n * sizeof(nxt_router_access_log_member_t)); + if (nxt_slow_path(member == NULL)) { + return NULL; + } + + next = 0; + + for (i = 0; i < n; i++) { + cv = nxt_conf_next_object_member(value, &name, &next); + if (cv == NULL) { + break; + } + + dst = nxt_str_dup(rtcf->mem_pool, &member[i].name, &name); + if (nxt_slow_path(dst == NULL)) { + return NULL; + } + + nxt_conf_get_string(cv, &str); + + member[i].tstr = nxt_tstr_compile(rtcf->tstr_state, &str, + NXT_TSTR_LOGGING); + if (nxt_slow_path(member[i].tstr == NULL)) { + return NULL; + } + } + + format->nmembers = n; + format->member = member; + + return format; + } + + size = nxt_conf_json_length(value, NULL); + + str.start = nxt_mp_nget(rtcf->mem_pool, size); + if (nxt_slow_path(str.start == NULL)) { + return NULL; + } + + str.length = nxt_conf_json_print(str.start, value, NULL) + - str.start; + + } else { + nxt_conf_get_string(value, &str); + } } else { str = default_format; @@ -180,7 +265,6 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, nxt_router_access_log_t *access_log, nxt_router_access_log_format_t *format) { nxt_int_t ret; - nxt_router_conf_t *rtcf; nxt_router_access_log_ctx_t *ctx; ctx = nxt_mp_get(r->mem_pool, sizeof(nxt_router_access_log_ctx_t)); @@ -190,8 +274,28 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, ctx->access_log = access_log; - if (nxt_tstr_is_const(format->tstr)) { - nxt_tstr_str(format->tstr, &ctx->text); + if (format->tstr != NULL) { + ret = nxt_router_access_log_text(task, r, ctx, format->tstr); + + } else { + ret = nxt_router_access_log_json(task, r, ctx, format); + } + + if (ret == NXT_OK) { + nxt_router_access_log_write(task, r, ctx); + } +} + + +static nxt_int_t +nxt_router_access_log_text(nxt_task_t *task, nxt_http_request_t *r, + nxt_router_access_log_ctx_t *ctx, nxt_tstr_t *tstr) +{ + nxt_int_t ret; + nxt_router_conf_t *rtcf; + + if (nxt_tstr_is_const(tstr)) { + nxt_tstr_str(tstr, &ctx->text); } else { rtcf = r->conf->socket_conf->router_conf; @@ -199,16 +303,76 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->tstr_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { - return; + return NXT_ERROR; } - ret = nxt_tstr_query(task, r->tstr_query, format->tstr, &ctx->text); + ret = nxt_tstr_query(task, r->tstr_query, tstr, &ctx->text); if (nxt_slow_path(ret != NXT_OK)) { - return; + return NXT_ERROR; } } - nxt_router_access_log_write(task, r, ctx); + return NXT_OK; +} + + +static nxt_int_t +nxt_router_access_log_json(nxt_task_t *task, nxt_http_request_t *r, + nxt_router_access_log_ctx_t *ctx, nxt_router_access_log_format_t *format) +{ + u_char *p; + size_t size; + nxt_int_t ret; + nxt_str_t str; + nxt_uint_t i; + nxt_conf_value_t *value; + nxt_router_conf_t *rtcf; + nxt_router_access_log_member_t *member; + + rtcf = r->conf->socket_conf->router_conf; + + value = nxt_conf_create_object(r->mem_pool, format->nmembers); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + for (i = 0; i < format->nmembers; i++) { + member = &format->member[i]; + + if (nxt_tstr_is_const(member->tstr)) { + nxt_tstr_str(member->tstr, &str); + + } else { + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->tstr_cache, r, r->mem_pool); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + ret = nxt_tstr_query(task, r->tstr_query, member->tstr, &str); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + nxt_conf_set_member_string(value, &member->name, &str, i); + } + + size = nxt_conf_json_length(value, NULL) + 1; + + p = nxt_mp_nget(r->mem_pool, size); + if (nxt_slow_path(p == NULL)) { + return NXT_ERROR; + } + + ctx->text.start = p; + + p = nxt_conf_json_print(p, value, NULL); + *p++ = '\n'; + + ctx->text.length = p - ctx->text.start; + + return NXT_OK; } |