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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
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
0dc8114
Add support to store status_codes to measure later
wpjunior Mar 4, 2025
7c7189e
Add support for measuring HTTP status codes in traffic status node
wpjunior Mar 4, 2025
5a423cf
Add support fordisplaying HTTP status codes in JSON and Prometheus fo…
wpjunior Mar 4, 2025
7b8b49c
fix status page when measure status code is enabled
wpjunior Mar 5, 2025
87dc9b3
refactor: define undefined status code slot constant for clarity
wpjunior Mar 5, 2025
5ba79f2
fix: include measure_all_status_codes check in status code filtering
wpjunior Mar 5, 2025
b23df54
feat: add support for merging HTTP status code counters in JSON and P…
wpjunior Mar 5, 2025
9380978
fix: correct status code separator logic in JSON display
wpjunior Mar 5, 2025
73ac93e
docs: add documentation for vhost_traffic_status_measure_status_codes…
wpjunior Mar 5, 2025
3f7bb46
use a independent metric for measure status codes
wpjunior Mar 5, 2025
eb0f188
add support to reset status codes using API
wpjunior Mar 7, 2025
b3c934a
Apply suggestions from code review
wpjunior Mar 9, 2025
997c866
Apply suggestions from code review[1]
wpjunior Mar 9, 2025
84c4b49
feat: add support for 'other' status code in traffic status display
wpjunior Mar 9, 2025
8617079
fix: adjust status code slot calculation to reserve slot for other st…
wpjunior Mar 9, 2025
80184fd
Apply suggestions from code review
wpjunior Mar 10, 2025
3d8bc68
refactor: update variable types and function signatures for consistency
wpjunior Mar 10, 2025
2a4b75a
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
67 changes: 49 additions & 18 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,16 +95,40 @@ ngx_http_vhost_traffic_status_display_set_server_node(
"display_set_server_node::escape_json_pool() failed");
}

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,
&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_cache_miss_counter,
vtsn->stat_cache_bypass_counter,
vtsn->stat_cache_expired_counter,
Expand Down Expand Up @@ -140,15 +168,7 @@ ngx_http_vhost_traffic_status_display_set_server_node(
vtsn->stat_cache_scarce_counter_oc,
vtsn->stat_request_time_counter_oc);
#else
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 @@ -215,6 +235,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 @@ -822,6 +848,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 @@ -139,6 +141,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 @@ -459,6 +468,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 @@ -256,6 +256,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