Skip to content

Add support to measure status_codes #317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
08f7e3c
Add support to store status_codes to measure later
wpjunior Mar 4, 2025
c54d9b9
Add support for measuring HTTP status codes in traffic status node
wpjunior Mar 4, 2025
cd05bf7
Add support fordisplaying HTTP status codes in JSON and Prometheus fo…
wpjunior Mar 4, 2025
54e29db
fix status page when measure status code is enabled
wpjunior Mar 5, 2025
255b55d
refactor: define undefined status code slot constant for clarity
wpjunior Mar 5, 2025
dcb24cc
fix: include measure_all_status_codes check in status code filtering
wpjunior Mar 5, 2025
d28faa5
feat: add support for merging HTTP status code counters in JSON and P…
wpjunior Mar 5, 2025
3f1dadc
fix: correct status code separator logic in JSON display
wpjunior Mar 5, 2025
9d1d15e
docs: add documentation for vhost_traffic_status_measure_status_codes…
wpjunior Mar 5, 2025
69d192e
use a independent metric for measure status codes
wpjunior Mar 5, 2025
c18cc20
add support to reset status codes using API
wpjunior Mar 7, 2025
2d9f59e
Apply suggestions from code review
wpjunior Mar 9, 2025
a0a72b6
Apply suggestions from code review[1]
wpjunior Mar 9, 2025
5f6c9a0
feat: add support for 'other' status code in traffic status display
wpjunior Mar 9, 2025
60af31a
fix: adjust status code slot calculation to reserve slot for other st…
wpjunior Mar 9, 2025
5fa4089
Apply suggestions from code review
wpjunior Mar 10, 2025
d35ee18
refactor: update variable types and function signatures for consistency
wpjunior Mar 10, 2025
f6ac339
restore empty lines
wpjunior Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,41 @@ http {
}
```

### vhost_traffic_status_measure_status_codes

Allows tracking of specific HTTP status codes or all status codes in the Vhost Traffic Status module.


| - | - |
| --- | --- |
| **Syntax** | vhost_traffic_status_measure_status_codes [all] [status_code1] [status_code2] ... |
| **Default** | off |
| **Context** | http |



#### Parameters
- `status_code1, status_code2, ...`: Specific HTTP status codes to track (100-599)
- `all`: Track all HTTP status codes

#### Examples

Track specific status codes:
```nginx
vhost_traffic_status_measure_status_codes 200 404 500;
```

Track all status codes:
```nginx
vhost_traffic_status_measure_status_codes all;
```

#### Description
- By default, no specific status code tracking is enabled
- Status codes must be in ascending order
- Only valid HTTP status codes between 100 and 599 are accepted
- When using `all`, every status code will be tracked

## Releases

To cut a release, create a changelog entry PR with [git-chglog](https://github.com/git-chglog/git-chglog)
Expand Down
71 changes: 51 additions & 20 deletions src/ngx_http_vhost_traffic_status_display_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,13 @@ ngx_http_vhost_traffic_status_display_set_server_node(
{
u_char *p, *c;
ngx_int_t rc;
ngx_uint_t i;
ngx_str_t tmp, dst;
ngx_uint_t *status_codes;
ngx_http_vhost_traffic_status_loc_conf_t *vtscf;
ngx_http_vhost_traffic_status_ctx_t *ctx;

ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module);
vtscf = ngx_http_get_module_loc_conf(r, ngx_http_vhost_traffic_status_module);

tmp = *key;
Expand Down Expand Up @@ -91,17 +95,42 @@ ngx_http_vhost_traffic_status_display_set_server_node(
"display_set_server_node::escape_json_pool() failed");
}

#if (NGX_HTTP_CACHE)
ngx_http_vhost_traffic_status_display_encode_uri(r, &dst);
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER,
&dst, vtsn->stat_request_counter,
vtsn->stat_in_bytes,
vtsn->stat_out_bytes,
vtsn->stat_1xx_counter,
vtsn->stat_2xx_counter,
vtsn->stat_3xx_counter,
vtsn->stat_4xx_counter,
vtsn->stat_5xx_counter,

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_START,
&dst, vtsn->stat_request_counter,
vtsn->stat_in_bytes,
vtsn->stat_out_bytes);

if (ctx->measure_status_codes != NULL && vtsn->stat_status_code_counter != NULL) {
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_START);

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_OTHER_STATUS_CODE,
vtsn->stat_status_code_counter[0]);

status_codes = (ngx_uint_t *) ctx->measure_status_codes->elts;

for (i = 0; i < ctx->measure_status_codes->nelts; i++) {
if (vtsn->stat_status_code_counter[i+1] == 0 && ctx->measure_all_status_codes) {
continue;
}
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE,
status_codes[i], vtsn->stat_status_code_counter[i+1]);
}

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_END);
}

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_MIDDLE,
vtsn->stat_1xx_counter,
vtsn->stat_2xx_counter,
vtsn->stat_3xx_counter,
vtsn->stat_4xx_counter,
vtsn->stat_5xx_counter);


#if (NGX_HTTP_CACHE)
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END,
vtsn->stat_cache_miss_counter,
vtsn->stat_cache_bypass_counter,
vtsn->stat_cache_expired_counter,
Expand Down Expand Up @@ -141,16 +170,7 @@ ngx_http_vhost_traffic_status_display_set_server_node(
vtsn->stat_cache_scarce_counter_oc,
vtsn->stat_request_time_counter_oc);
#else
ngx_http_vhost_traffic_status_display_encode_uri(r, &dst);
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER,
&dst, vtsn->stat_request_counter,
vtsn->stat_in_bytes,
vtsn->stat_out_bytes,
vtsn->stat_1xx_counter,
vtsn->stat_2xx_counter,
vtsn->stat_3xx_counter,
vtsn->stat_4xx_counter,
vtsn->stat_5xx_counter,
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END,
vtsn->stat_request_time_counter,
ngx_http_vhost_traffic_status_node_time_queue_average(
&vtsn->stat_request_times, vtscf->average_method,
Expand Down Expand Up @@ -217,6 +237,12 @@ ngx_http_vhost_traffic_status_display_set_server(ngx_http_request_t *r,
&vtscf->stats.stat_request_times,
&vtsn->stat_request_times, vtscf->average_period);

if (ctx->measure_status_codes != NULL && vtsn->stat_status_code_counter != NULL) {
ngx_http_vhost_traffic_status_status_code_merge(
vtscf->stats.stat_status_code_counter,
vtsn->stat_status_code_counter, ctx->measure_status_codes->nelts+1);
}

vtscf->stats.stat_request_counter_oc += vtsn->stat_request_counter_oc;
vtscf->stats.stat_in_bytes_oc += vtsn->stat_in_bytes_oc;
vtscf->stats.stat_out_bytes_oc += vtsn->stat_out_bytes_oc;
Expand Down Expand Up @@ -855,6 +881,11 @@ ngx_http_vhost_traffic_status_display_set(ngx_http_request_t *r,
ngx_memzero(&vtscf->stats, sizeof(vtscf->stats));
ngx_http_vhost_traffic_status_node_time_queue_init(&vtscf->stats.stat_request_times);

if (ctx->measure_status_codes != NULL) {
vtscf->stats.stat_status_code_counter = ngx_pcalloc(r->pool, sizeof(ngx_atomic_t) * (ctx->measure_status_codes->nelts +1));
vtscf->stats.stat_status_code_length = ctx->measure_status_codes->nelts;
}

/* main & connections */
buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_S);

Expand Down
31 changes: 14 additions & 17 deletions src/ngx_http_vhost_traffic_status_display_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,20 @@

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_S "\"serverZones\":{"

#if (NGX_HTTP_CACHE)
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER "\"%V\":{" \
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_START "\"%V\":{" \
"\"requestCounter\":%uA," \
"\"inBytes\":%uA," \
"\"outBytes\":%uA," \
"\"responses\":{" \
"\"outBytes\":%uA,"

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_MIDDLE "\"responses\":{" \
"\"1xx\":%uA," \
"\"2xx\":%uA," \
"\"3xx\":%uA," \
"\"4xx\":%uA," \
"\"5xx\":%uA," \
"\"miss\":%uA," \
"\"5xx\":%uA"

#if (NGX_HTTP_CACHE)
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END ",\"miss\":%uA," \
"\"bypass\":%uA," \
"\"expired\":%uA," \
"\"stale\":%uA," \
Expand Down Expand Up @@ -92,17 +94,7 @@
"}" \
"},"
#else
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER "\"%V\":{" \
"\"requestCounter\":%uA," \
"\"inBytes\":%uA," \
"\"outBytes\":%uA," \
"\"responses\":{" \
"\"1xx\":%uA," \
"\"2xx\":%uA," \
"\"3xx\":%uA," \
"\"4xx\":%uA," \
"\"5xx\":%uA" \
"}," \
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END "}," \
"\"requestMsecCounter\":%uA," \
"\"requestMsec\":%M," \
"\"requestMsecs\":{" \
Expand All @@ -128,6 +120,11 @@
"},"
#endif

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_START "\"statusCodes\":{"
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE ",\"%uA\":%uA"
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_OTHER_STATUS_CODE "\"other\":%uA"
#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_END "}, "

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_FILTER_S "\"filterZones\":{"

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_UPSTREAM_S "\"upstreamZones\":{"
Expand Down
29 changes: 29 additions & 0 deletions src/ngx_http_vhost_traffic_status_display_prometheus.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ ngx_http_vhost_traffic_status_display_prometheus_set_server_node(
ngx_str_t server;
ngx_uint_t i, n;
ngx_http_vhost_traffic_status_loc_conf_t *vtscf;
ngx_http_vhost_traffic_status_ctx_t *ctx;
ngx_http_vhost_traffic_status_node_histogram_bucket_t *b;

ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module);
vtscf = ngx_http_get_module_loc_conf(r, ngx_http_vhost_traffic_status_module);

server = *key;
Expand All @@ -75,6 +77,23 @@ ngx_http_vhost_traffic_status_display_prometheus_set_server_node(
&vtsn->stat_request_times, vtscf->average_method,
vtscf->average_period) / 1000);


if (ctx->measure_status_codes != NULL && vtsn->stat_status_code_counter != NULL) {
ngx_uint_t *status_codes = (ngx_uint_t *) ctx->measure_status_codes->elts;

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_PROMETHEUS_FMT_SERVER_OTHER_STATUS_CODE,
&server, vtsn->stat_status_code_counter[0]);

for (i = 0; i < ctx->measure_status_codes->nelts; i++) {
if (vtsn->stat_status_code_counter[i+1] == 0 && ctx->measure_all_status_codes) {
continue;
}

buf = ngx_sprintf(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_PROMETHEUS_FMT_SERVER_STATUS_CODE,
&server, status_codes[i], vtsn->stat_status_code_counter[i+1]);
}
}

/* histogram */
b = &vtsn->stat_request_buckets;

Expand Down Expand Up @@ -157,6 +176,11 @@ ngx_http_vhost_traffic_status_display_prometheus_set_server(ngx_http_request_t *
&vtscf->stats.stat_request_times,
&vtsn->stat_request_times, vtscf->average_period);

if (ctx->measure_status_codes != NULL && vtsn->stat_status_code_counter != NULL) {
ngx_http_vhost_traffic_status_status_code_merge(vtscf->stats.stat_status_code_counter,
vtsn->stat_status_code_counter, ctx->measure_status_codes->nelts+1);
}

#if (NGX_HTTP_CACHE)
vtscf->stats.stat_cache_miss_counter +=
vtsn->stat_cache_miss_counter;
Expand Down Expand Up @@ -497,6 +521,11 @@ ngx_http_vhost_traffic_status_display_prometheus_set(ngx_http_request_t *r,
ngx_memzero(&vtscf->stats, sizeof(vtscf->stats));
ngx_http_vhost_traffic_status_node_time_queue_init(&vtscf->stats.stat_request_times);

if (ctx->measure_status_codes != NULL) {
vtscf->stats.stat_status_code_counter = ngx_pcalloc(r->pool, sizeof(ngx_atomic_t) * (ctx->measure_status_codes->nelts+1));
vtscf->stats.stat_status_code_length = ctx->measure_status_codes->nelts;
}

/* main & connections */
buf = ngx_http_vhost_traffic_status_display_prometheus_set_main(r, buf);

Expand Down
8 changes: 8 additions & 0 deletions src/ngx_http_vhost_traffic_status_display_prometheus.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"# TYPE nginx_vts_server_bytes_total counter\n" \
"# HELP nginx_vts_server_requests_total The requests counter\n" \
"# TYPE nginx_vts_server_requests_total counter\n" \
"# HELP nginx_vts_status_code_requests_total The requests counter by status code \n" \
"# TYPE nginx_vts_status_code_requests_total counter\n" \
"# HELP nginx_vts_server_request_seconds_total The request processing " \
"time in seconds\n" \
"# TYPE nginx_vts_server_request_seconds_total counter\n" \
Expand All @@ -56,6 +58,12 @@
"nginx_vts_server_request_seconds_total{host=\"%V\"} %.3f\n" \
"nginx_vts_server_request_seconds{host=\"%V\"} %.3f\n"

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_PROMETHEUS_FMT_SERVER_OTHER_STATUS_CODE \
"nginx_vts_status_code_requests_total{host=\"%V\",code=\"other\"} %uA\n"

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_PROMETHEUS_FMT_SERVER_STATUS_CODE \
"nginx_vts_status_code_requests_total{host=\"%V\",code=\"%d\"} %uA\n"

#define NGX_HTTP_VHOST_TRAFFIC_STATUS_PROMETHEUS_FMT_SERVER_HISTOGRAM_BUCKET \
"nginx_vts_server_request_duration_seconds_bucket{host=\"%V\"," \
"le=\"%.3f\"} %uA\n"
Expand Down
72 changes: 72 additions & 0 deletions src/ngx_http_vhost_traffic_status_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ static void ngx_http_vhost_traffic_status_rbtree_insert_value(
ngx_rbtree_node_t *sentinel);
static ngx_int_t ngx_http_vhost_traffic_status_init_zone(
ngx_shm_zone_t *shm_zone, void *data);
static char *ngx_http_vhost_traffic_status_measure_status_codes(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_vhost_traffic_status_zone(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_vhost_traffic_status_dump(ngx_conf_t *cf,
Expand Down Expand Up @@ -148,6 +150,13 @@ static ngx_command_t ngx_http_vhost_traffic_status_commands[] = {
0,
NULL },

{ ngx_string("vhost_traffic_status_measure_status_codes"),
NGX_HTTP_MAIN_CONF|NGX_CONF_NOARGS|NGX_CONF_1MORE,
ngx_http_vhost_traffic_status_measure_status_codes,
0,
0,
NULL },

{ ngx_string("vhost_traffic_status_dump"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
ngx_http_vhost_traffic_status_dump,
Expand Down Expand Up @@ -475,6 +484,69 @@ ngx_http_vhost_traffic_status_init_zone(ngx_shm_zone_t *shm_zone, void *data)
}


static char *
ngx_http_vhost_traffic_status_measure_status_codes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_vhost_traffic_status_ctx_t *ctx;
ngx_str_t *value;
ngx_int_t n;
ngx_int_t previous_n;
ngx_uint_t i;
ngx_int_t *status_code;

ctx = ngx_http_conf_get_module_main_conf(cf, ngx_http_vhost_traffic_status_module);
if (ctx == NULL) {
return NGX_CONF_ERROR;
}

ctx->measure_status_codes = ngx_array_create(cf->pool, 1, sizeof(ngx_int_t));
previous_n = 0;
value = cf->args->elts;

/* arguments process */
if (ngx_strncmp(value[1].data, "all", 3) == 0) {
for (n = 100; n < 600; n++) {
status_code = ngx_array_push(ctx->measure_status_codes);
if (status_code == NULL) {
return NGX_CONF_ERROR;
}
*status_code = n;
}
ctx->measure_all_status_codes = 1;
return NGX_OK;
}
for (i = 1; i < cf->args->nelts; i++) {
n = ngx_atoi(value[i].data, value[i].len);
if (n == NGX_ERROR || n == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}

if (n < previous_n) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "status codes must be ordered");
return NGX_CONF_ERROR;
}

if (n < 100 || n > 599) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid status_code \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}

status_code = ngx_array_push(ctx->measure_status_codes);
if (status_code == NULL) {
return NGX_CONF_ERROR;
}

*status_code = n;
previous_n = n;
}

ctx->measure_all_status_codes = 0;

return NGX_CONF_OK;
}


static char *
ngx_http_vhost_traffic_status_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
Expand Down
3 changes: 3 additions & 0 deletions src/ngx_http_vhost_traffic_status_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ typedef struct {
ngx_str_t dump_file;
ngx_msec_t dump_period;
ngx_event_t dump_event;

ngx_flag_t measure_all_status_codes;
ngx_array_t *measure_status_codes;
} ngx_http_vhost_traffic_status_ctx_t;


Expand Down
Loading