Skip to content

Commit

Permalink
http: support JSON format in access log
Browse files Browse the repository at this point in the history
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"
      }
    }
  }
  • Loading branch information
hongzhidao committed Oct 29, 2024
1 parent d5f3b57 commit 7339197
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 16 deletions.
51 changes: 50 additions & 1 deletion src/nxt_conf_validation.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1418,7 +1423,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,
Expand Down Expand Up @@ -3523,3 +3529,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 only string.");
}

nxt_conf_get_string(value, &str);

if (nxt_is_tstr(&str)) {
return nxt_conf_vldt_var(vldt, name, &str);
}

return NXT_OK;
}
194 changes: 179 additions & 15 deletions src/nxt_router_access_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};


Expand All @@ -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,
Expand Down Expand Up @@ -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 - - "
Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -190,25 +274,105 @@ 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;

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;
}


Expand Down

0 comments on commit 7339197

Please sign in to comment.