diff --git a/auto/modules b/auto/modules index 9bc2e04..72cb16b 100644 --- a/auto/modules +++ b/auto/modules @@ -325,6 +325,13 @@ if [ $HTTP_STUB_STATUS = YES ]; then HTTP_SRCS="$HTTP_SRCS src/http/modules/ngx_http_stub_status_module.c" fi +if [ $NGX_STATUS = YES ]; then + if [ $HTTP_STATUS_TXT = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_STATUS_TXT_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_STATUS_TXT_SRCS" + fi +fi + #if [ -r $NGX_OBJS/auto ]; then # . $NGX_OBJS/auto #fi @@ -424,6 +431,14 @@ if [ $NGX_CPP_TEST = YES ]; then fi +if [ $NGX_STATUS = YES ]; then + have=NGX_STATUS . auto/have + modules="$modules $NGX_STATUS_MODULE" + CORE_SRCS="$CORE_SRCS $NGX_STATUS_SRCS" + CORE_DEPS="$CORE_DEPS $NGX_STATUS_DEPS" +fi + + cat << END > $NGX_MODULES_C #include diff --git a/auto/options b/auto/options index 3b3ee0f..3c2b2b5 100644 --- a/auto/options +++ b/auto/options @@ -131,6 +131,9 @@ NGX_CPP_TEST=NO NGX_CPU_CACHE_LINE= +NGX_STATUS=YES +HTTP_STATUS_TXT=YES + opt= for option @@ -260,6 +263,9 @@ do --with-zlib-opt=*) ZLIB_OPT="$value" ;; --with-zlib-asm=*) ZLIB_ASM="$value" ;; + --without-status) NGX_STATUS=NO ;; + --without-http_status_txt) NGX_HTTP_STATUS_TXT=NO ;; + --test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;; --test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;; --test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;; @@ -394,6 +400,9 @@ cat << END --with-openssl=DIR set path to OpenSSL library sources --with-openssl-opt=OPTIONS set additional options for OpenSSL building + --without-status disable status + --without-http_status_txt disable http_status_txt + --with-debug enable the debugging logging END diff --git a/auto/sources b/auto/sources index e4649b0..cd3f2ae 100644 --- a/auto/sources +++ b/auto/sources @@ -484,3 +484,10 @@ NGX_GOOGLE_PERFTOOLS_MODULE=ngx_google_perftools_module NGX_GOOGLE_PERFTOOLS_SRCS=src/misc/ngx_google_perftools_module.c NGX_CPP_TEST_SRCS=src/misc/ngx_cpp_test_module.cpp + +NGX_STATUS_MODULE=ngx_status_module +NGX_STATUS_SRCS=src/core/ngx_status.c +NGX_STATUS_DEPS=src/core/ngx_status.h + +HTTP_STATUS_TXT_MODULE=ngx_http_status_txt_module +HTTP_STATUS_TXT_SRCS=src/http/modules/ngx_http_status_txt_module.c diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h index d5f18b8..e4b07aa 100644 --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -75,7 +75,9 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); #include #include #include - +#if (NGX_PCRE) +#include +#endif #define LF (u_char) 10 #define CR (u_char) 13 diff --git a/src/core/ngx_status.c b/src/core/ngx_status.c new file mode 100644 index 0000000..982d192 --- /dev/null +++ b/src/core/ngx_status.c @@ -0,0 +1,289 @@ + +/* + * Copyright (C) Kirill A. Korinskiy + */ + + +#include +#include + + +static void *ngx_status_module_create_conf(ngx_cycle_t *cycle); +static char *ngx_status_module_init_conf(ngx_cycle_t *cycle, void *conf); + +static ngx_int_t ngx_status_module_init(ngx_cycle_t *cycle); + +typedef struct { + ngx_uint_t window; + ngx_uint_t count; + ngx_status_list_t **counters; +} ngx_status_conf_t; + + +static ngx_command_t ngx_status_commands[] = { + + { ngx_string("status_window"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_status_conf_t, window), + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_status_module_ctx = { + ngx_string("ngx_status"), + ngx_status_module_create_conf, + ngx_status_module_init_conf +}; + + +ngx_module_t ngx_status_module = { + NGX_MODULE_V1, + &ngx_status_module_ctx, /* module context */ + ngx_status_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + ngx_status_module_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_status_add_counter(ngx_cycle_t *cycle, ngx_module_t *module, + ngx_status_counter_t *counter) +{ + ngx_status_conf_t *conf; + ngx_status_list_t *c, *cc; + + conf = (ngx_status_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_status_module); + + c = ngx_palloc(cycle->pool, sizeof(ngx_status_list_t)); + if (c == NULL) { + return NGX_ERROR; + } + + c->counter = counter; + c->next = NULL; + + if (conf->counters[module->index] == NULL) { + conf->counters[module->index] = c; + } else { + cc = conf->counters[module->index]; + + for (; cc->next; cc = cc->next); + + cc->next = c; + } + + conf->count++; + + return NGX_OK; +} + + +ngx_int_t +ngx_status_add_child(ngx_cycle_t *cycle, ngx_status_counter_t *parent, + ngx_status_counter_t *child) +{ + ngx_status_conf_t *conf; + ngx_status_list_t *c, *cc; + + conf = (ngx_status_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_status_module); + + c = ngx_palloc(cycle->pool, sizeof(ngx_status_list_t)); + if (c == NULL) { + return NGX_ERROR; + } + + c->counter = child; + c->next = NULL; + + if (parent->childs == NULL) { + parent->childs = c; + } else { + cc = parent->childs; + + for (; cc->next; cc = cc->next); + + cc->next = c; + } + + conf->count++; + + return NGX_OK; +} + + +ngx_int_t +ngx_status_fetch_add(ngx_status_counter_t *counter) +{ + ngx_atomic_t *window; + ngx_status_conf_t *conf; + + conf = (ngx_status_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_status_module); + + window = counter->windows + (ngx_time() % (2 * conf->window)); + + ngx_atomic_fetch_add(window, 1); + + if (window - conf->window > counter->windows) { + *(window - conf->window) = 0; + } else { + *(window + conf->window) = 0; + } + + ngx_atomic_fetch_add(counter->accumulate, 1); + + return NGX_OK; +} + + +ngx_int_t +ngx_status_get_accumulate_value(ngx_status_counter_t *counter) +{ + return *(counter->accumulate); +} + + +ngx_int_t +ngx_status_get_periodic_value(ngx_status_counter_t *counter, ngx_uint_t period) +{ + ngx_int_t sum ; + ngx_atomic_t *start, *last; + ngx_status_conf_t *conf; + + + conf = (ngx_status_conf_t *) + ngx_get_conf(ngx_cycle->conf_ctx, ngx_status_module); + + if (period > conf->window) { + period = conf->window; + } + + sum = 0; + last = counter->windows + 2 * conf->window; + start = counter->windows + (ngx_time() % (2 * conf->window)); + + for (; period; period--) { + sum += *start; + if (start - 1 < counter->windows) { + start = last; + } else { + start--; + } + } + + return sum; +} + + +ngx_status_list_t ** +ngx_status_get_counters(ngx_cycle_t *cycle) +{ + return ((ngx_status_conf_t *) + ngx_get_conf(cycle->conf_ctx, ngx_status_module))->counters; +} + + +static void * +ngx_status_module_create_conf(ngx_cycle_t *cycle) +{ + ngx_status_conf_t *scf; + ngx_uint_t i; + + scf = ngx_pcalloc(cycle->pool, sizeof(ngx_status_conf_t)); + if (scf == NULL) { + return NULL; + } + + scf->window = NGX_CONF_UNSET_UINT; + + for (i = 0; ngx_modules[i]; i++); + + scf->counters = ngx_pcalloc(cycle->pool, sizeof(ngx_status_list_t) * i); + if (scf->counters == NULL) { + return NULL; + } + + scf->count = 0; + + return scf; +} + + +static char * +ngx_status_module_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_status_conf_t *scf = conf; + + ngx_conf_init_uint_value(scf->window, 300); + + return NGX_CONF_OK; +} + +static ngx_uint_t +ngx_status_set_associating_memory(u_char *shared, size_t cl, + ngx_uint_t count, ngx_uint_t window, ngx_status_list_t *c, ngx_uint_t j) +{ + while (c) { + c->counter->accumulate = (ngx_atomic_t *)(shared + (j * cl)); + c->counter->windows = (ngx_atomic_t *)(shared + (count * cl) + + (2 * j * cl * window)); + + j = ngx_status_set_associating_memory(shared, cl, count, window, + c->counter->childs, j + 1); + + c = c->next; + } + + return j; +} + + +static ngx_int_t +ngx_status_module_init(ngx_cycle_t *cycle) +{ + u_char *shared; + size_t size, cl; + ngx_shm_t shm; + ngx_uint_t i, j; + ngx_status_conf_t *conf; + + conf = (ngx_status_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_status_module); + + if (!conf->count) { + return NGX_OK; + } + + /* cl should be equal or bigger than cache line size */ + cl = 128; + + size = conf->count * cl + 2 * conf->window * conf->count * cl; + + shm.size = size; + shm.name.len = sizeof("nginx_status_shared_zone"); + shm.name.data = (u_char *) "nginx_status_shared_zone"; + shm.log = cycle->log; + + if (ngx_shm_alloc(&shm) != NGX_OK) { + return NGX_ERROR; + } + + shared = shm.addr; + + for (i = 0, j = 0; ngx_modules[i]; i++) { + j = ngx_status_set_associating_memory(shared, cl, conf->count, + conf->window, conf->counters[ngx_modules[i]->index], j); + } + + return NGX_OK; +} diff --git a/src/core/ngx_status.h b/src/core/ngx_status.h new file mode 100644 index 0000000..b6da09b --- /dev/null +++ b/src/core/ngx_status.h @@ -0,0 +1,49 @@ + +/* + * Copyright (C) Kirill A. Korinskiy + */ + + +#ifndef _NGX_STATUS_H_INCLUDED_ +#define _NGX_STATUS_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_status_list_s ngx_status_list_t; + +typedef struct { + ngx_str_t name; + ngx_str_t caption; + ngx_str_t label_for_childs; + ngx_atomic_t *accumulate; + ngx_atomic_t *windows; + ngx_status_list_t *childs; +} ngx_status_counter_t; + +struct ngx_status_list_s { + ngx_status_counter_t *counter; + ngx_status_list_t *next; +}; + +extern ngx_module_t ngx_status_module; + + +ngx_int_t ngx_status_add_counter(ngx_cycle_t *cycle, ngx_module_t *module, + ngx_status_counter_t *counter); + +ngx_int_t ngx_status_add_child(ngx_cycle_t *cycle, ngx_status_counter_t *parent, + ngx_status_counter_t *child); + +ngx_int_t ngx_status_fetch_add(ngx_status_counter_t *counter); + +ngx_int_t ngx_status_get_accumulate_value(ngx_status_counter_t *counter); +ngx_int_t ngx_status_get_periodic_value(ngx_status_counter_t *counter, + ngx_uint_t period); + +ngx_status_list_t **ngx_status_get_counters(ngx_cycle_t *cycle); + + +#endif /* _NGX_STATUS_H_INCLUDED_ */ diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c index e30c563..dac0c01 100644 --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -57,7 +57,7 @@ ngx_int_t ngx_accept_disabled; ngx_file_t ngx_accept_mutex_lock_file; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_t ngx_stat_accepted0; ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0; @@ -494,7 +494,7 @@ ngx_event_module_init(ngx_cycle_t *cycle) size = cl /* ngx_accept_mutex */ + cl; /* ngx_connection_counter */ -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) size += cl /* ngx_stat_accepted */ + cl /* ngx_stat_handled */ @@ -526,7 +526,7 @@ ngx_event_module_init(ngx_cycle_t *cycle) ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_stat_accepted = (ngx_atomic_t *) (shared + 2 * cl); ngx_stat_handled = (ngx_atomic_t *) (shared + 3 * cl); diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index 33c8cdc..791beaa 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -477,7 +477,7 @@ extern ngx_msec_t ngx_accept_mutex_delay; extern ngx_int_t ngx_accept_disabled; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) extern ngx_atomic_t *ngx_stat_accepted; extern ngx_atomic_t *ngx_stat_handled; diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c index a17d630..bc75a5a 100644 --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -74,7 +74,7 @@ ngx_event_accept(ngx_event_t *ev) return; } -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_accepted, 1); #endif @@ -92,7 +92,7 @@ ngx_event_accept(ngx_event_t *ev) return; } -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_active, 1); #endif @@ -187,7 +187,7 @@ ngx_event_accept(ngx_event_t *ev) c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif @@ -378,7 +378,7 @@ ngx_close_accepted_connection(ngx_connection_t *c) ngx_destroy_pool(c->pool); } -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif } diff --git a/src/http/modules/ngx_http_status_txt_module.c b/src/http/modules/ngx_http_status_txt_module.c new file mode 100644 index 0000000..2752c30 --- /dev/null +++ b/src/http/modules/ngx_http_status_txt_module.c @@ -0,0 +1,419 @@ + +/* + * Copyright (C) Kirill A. Korinskiy + */ + + +#include +#include +#include +#include + + +typedef struct { + ngx_int_t num; + ngx_str_t value; +} ngx_http_status_txt_period_t; + +typedef struct { + ngx_array_t *periods; +} ngx_http_status_txt_loc_conf_t; + + +static void *ngx_http_status_txt_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_status_txt_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); + +static char *ngx_http_status_txt(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static ngx_command_t ngx_http_status_commands[] = { + + { ngx_string("status_txt"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_status_txt, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_status_txt_loc_conf_t, periods), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_status_txt_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_status_txt_create_loc_conf, /* create location configuration */ + ngx_http_status_txt_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_status_txt_module = { + NGX_MODULE_V1, + &ngx_http_status_txt_module_ctx, /* module context */ + ngx_http_status_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + +static ngx_time_t last_configure; +static ngx_str_t last_configure_human_time; + + +static ngx_int_t +ngx_http_status_calcualte_len(ngx_status_list_t *c, + ngx_http_status_txt_period_t *period, ngx_uint_t level) +{ + ngx_uint_t size = 0; + + while (c) { + size += level * (sizeof(" ") - 1); + size += sizeof(": ()\n") - 1 + c->counter->caption.len + + NGX_INT_T_LEN + NGX_INT_T_LEN; + + if (!level) { + if (period->num) { + size += sizeof(", for last seconds") - 1 + period->value.len; + } else { + size += sizeof(", momentary") - 1; + } + } + + if (c->counter->childs) { + if (c->counter->label_for_childs.len) { + size += (level + 1) * (sizeof(" ") - 1) + sizeof(":\n") - 1 + + c->counter->label_for_childs.len; + size += ngx_http_status_calcualte_len(c->counter->childs, + period, level + 2); + } + size += ngx_http_status_calcualte_len(c->counter->childs, period, + level + 1); + } + + c = c->next; + } + + return size; +} + + +static u_char* +ngx_http_status_write_counter(u_char *p, ngx_status_list_t *c, + ngx_http_status_txt_period_t *period, ngx_uint_t level) +{ + ngx_int_t periodic_value; + ngx_uint_t i; + + while (c) { + for (i = level; i; i--) { + *(p++) = ' '; + } + + p = ngx_sprintf(p, "%V", &c->counter->caption); + + if (!level) { + if (period->num) { + p = ngx_sprintf(p, ", for last %V seconds", &period->value); + } else { + p = ngx_sprintf(p, ", momentary", &period->value); + } + } + + periodic_value = ngx_status_get_periodic_value(c->counter, + period->num ? period->num : 1); + + if (period->num) { + p = ngx_sprintf(p, ": %uA(%uA)", + ngx_status_get_periodic_value(c->counter, + period->num), + periodic_value/period->num); + } else { + p = ngx_sprintf(p, ": %uA(%uA)", + ngx_status_get_accumulate_value(c->counter), + periodic_value); + } + + *(p++) = '\n'; + + if (c->counter->childs) { + if (c->counter->label_for_childs.len) { + for (i = level + 1; i; i--) { + *(p++) = ' '; + } + p = ngx_sprintf(p, "%V:\n", &c->counter->label_for_childs); + p = ngx_http_status_write_counter(p, c->counter->childs, + period, level + 2); + } else { + p = ngx_http_status_write_counter(p, c->counter->childs, + period, level + 1); + } + } + + c = c->next; + } + + return p; +} + + +static ngx_int_t ngx_http_status_txt_handler(ngx_http_request_t *r) +{ + size_t size; + ngx_int_t rc; + ngx_buf_t *b; + ngx_uint_t i, j; + ngx_chain_t out; + + ngx_atomic_int_t ap, hn, ac, rq, rd, wr; + + ngx_status_list_t **counters, *c; + + ngx_http_core_srv_conf_t *cscf; + + ngx_uint_t uptime, uptime_days, uptime_hours, uptime_minutes, uptime_seconds; + + ngx_http_status_txt_period_t *period; + ngx_http_status_txt_loc_conf_t *conf; + + if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { + return NGX_HTTP_NOT_ALLOWED; + } + + rc = ngx_http_discard_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_status_txt_module); + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + if (cscf == NULL) { + return NGX_ERROR; + } + + uptime = ngx_time() - last_configure.sec; + uptime_days = uptime / 86400; + uptime -= uptime_days * 86400; + uptime_hours = uptime / 3600; + uptime -= uptime_hours * 3600; + uptime_minutes = uptime / 60; + uptime -= uptime_minutes * 60; + uptime_seconds = uptime; + uptime = ngx_time() - last_configure.sec; + + + counters = ngx_status_get_counters((ngx_cycle_t *)ngx_cycle); + + r->headers_out.content_type.len = sizeof("text/plain") - 1; + r->headers_out.content_type.data = (u_char *) "text/plain"; + + if (r->method == NGX_HTTP_HEAD) { + r->headers_out.status = NGX_HTTP_OK; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + } + + size = sizeof("Server " NGINX_VER " \n") - 1 + + cscf->server_name.len + + sizeof("Last config reload: ()\n") - 1 + NGX_INT_T_LEN + + last_configure_human_time.len + + sizeof("Uptime: (d h m s)\n") - 1 + NGX_INT_T_LEN + + NGX_INT_T_LEN + NGX_INT_T_LEN + NGX_INT_T_LEN + NGX_INT_T_LEN; + + size += sizeof("\nActive connections: \n") + NGX_ATOMIC_T_LEN + + sizeof("server accepts handled requests\n") - 1 + + 6 + 3 * NGX_ATOMIC_T_LEN + + sizeof("Reading: Writing: Waiting: \n") + 3 * NGX_ATOMIC_T_LEN; + + for (i = 0; ngx_modules[i]; i++) { + c = counters[ngx_modules[i]->index]; + + if (c == NULL) { + continue; + } + + for (j = 0; j < conf->periods->nelts; j++) { + period = conf->periods->elts; + + size += sizeof("\n") - 1; + + size += ngx_http_status_calcualte_len(c, &period[j], 0); + } + } + + + b = ngx_create_temp_buf(r->pool, size); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + out.buf = b; + out.next = NULL; + + b->last = ngx_sprintf(b->last, "Server " NGINX_VER " %V\n", + &cscf->server_name); + + b->last = ngx_sprintf(b->last, "Last config reload: %uA (%V)\n", + last_configure.sec, + &last_configure_human_time); + + b->last = ngx_sprintf(b->last, "Uptime: %uA (%uAd %uAh %uAm %uAs)\n", + uptime, uptime_days, uptime_hours, + uptime_minutes, uptime_seconds); + + ap = *ngx_stat_accepted; + hn = *ngx_stat_handled; + ac = *ngx_stat_active; + rq = *ngx_stat_requests; + rd = *ngx_stat_reading; + wr = *ngx_stat_writing; + + b->last = ngx_sprintf(b->last, "\nActive connections: %uA \n", ac); + + b->last = ngx_cpymem(b->last, "server accepts handled requests\n", + sizeof("server accepts handled requests\n") - 1); + + b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq); + + b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n", + rd, wr, ac - (rd + wr)); + + + for (i = 0; ngx_modules[i]; i++) { + c = counters[ngx_modules[i]->index]; + + if (c == NULL) { + continue; + } + + for (j = 0; j < conf->periods->nelts; j++) { + period = conf->periods->elts; + + b->last = ngx_sprintf(b->last, "\n"); + + b->last = ngx_http_status_write_counter(b->last, c, &period[j], 0); + } + } + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.content_length_n = b->last - b->pos; + + b->last_buf = 1; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + return ngx_http_output_filter(r, &out); +} + + +static void * +ngx_http_status_txt_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_status_txt_loc_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_status_txt_loc_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + return conf; +} + + +static char * +ngx_http_status_txt_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_status_txt_loc_conf_t *prev = parent; + ngx_http_status_txt_loc_conf_t *conf = child; + + if (conf->periods == NULL) { + conf->periods = prev->periods; + } + + return NGX_CONF_OK; +} + + +static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + +static char *ngx_http_status_txt(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p = conf; + ngx_tm_t tm; + ngx_str_t *value; + ngx_uint_t i; + ngx_array_t **periods; + + ngx_http_core_loc_conf_t *clcf; + + ngx_http_status_txt_period_t *period; + + periods = (ngx_array_t **) (p + cmd->offset); + + value = cf->args->elts; + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_status_txt_handler; + + last_configure = *ngx_cached_time; + last_configure_human_time.len = sizeof("28/May/1987 17:30:00 +0400") - 1; + last_configure_human_time.data = ngx_palloc(cf->pool, + last_configure_human_time.len); + if (last_configure_human_time.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_gmtime(ngx_cached_time->sec + ngx_cached_time->gmtoff * 60, &tm); + + (void) ngx_sprintf(last_configure_human_time.data, + "%02d/%s/%d %02d:%02d:%02d %c%02d%02d", + tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + ngx_cached_time->gmtoff < 0 ? '-' : '+', + ngx_abs(ngx_cached_time->gmtoff / 60), + ngx_abs(ngx_cached_time->gmtoff % 60)); + + *periods = ngx_array_create(cf->pool, cf->args->nelts - 1, + sizeof(ngx_http_status_txt_period_t)); + if (*periods == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 1; i < cf->args->nelts; i++) { + period = ngx_array_push(*periods); + period->value = value[i]; + period->num = ngx_atoi(period->value.data, period->value.len); + + if (period->num == NGX_ERROR) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 73d7af9..31bbe90 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -838,6 +838,10 @@ ngx_http_core_find_config_phase(ngx_http_request_t *r, clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); +#if (NGX_STATUS) + ngx_status_fetch_add(clcf->request_counter); +#endif + if (!r->internal && clcf->internal) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; @@ -2387,9 +2391,15 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) *cscfp = cscf; +#if (NGX_STATUS) + cscf->request_counter = + ngx_pcalloc(cf->pool, sizeof(ngx_status_counter_t)); + if (cscf->request_counter == NULL) { + return NGX_CONF_ERROR; + } +#endif /* parse inside server{} */ - pcf = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_SRV_CONF; @@ -2398,6 +2408,55 @@ ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) *cf = pcf; +#if (NGX_STATUS) + cscf->request_counter->label_for_childs.len = sizeof("locations info") - 1; + cscf->request_counter->label_for_childs.data = (u_char *)"locations info"; + cscf->request_counter->name.len = sizeof("request_host_") - 1; + cscf->request_counter->caption.len = sizeof("Request for host ") - 1; + + if (cscf->server_name.len) { + cscf->request_counter->name.len += cscf->server_name.len; + cscf->request_counter->caption.len += cscf->server_name.len; + } else { + cscf->request_counter->name.len += sizeof("default") - 1; + cscf->request_counter->caption.len += sizeof("default") - 1; + } + + cscf->request_counter->name.data = + ngx_palloc(cf->pool, cscf->request_counter->name.len); + if (cscf->request_counter->name.data == NULL) { + return NGX_CONF_ERROR; + } + + cscf->request_counter->caption.data = + ngx_palloc(cf->pool, cscf->request_counter->caption.len); + if (cscf->request_counter->caption.data == NULL) { + return NGX_CONF_ERROR; + } + + if (cscf->server_name.len) { + ngx_sprintf(cscf->request_counter->name.data, + "request_host_%V", + &cscf->server_name); + + ngx_sprintf(cscf->request_counter->caption.data, + "Request for host %V", + &cscf->server_name); + } else { + ngx_sprintf(cscf->request_counter->name.data, + "request_host_default"); + + ngx_sprintf(cscf->request_counter->caption.data, + "Request for host default"); + } + + if (ngx_status_add_counter(cf->cycle, &ngx_http_module, + cscf->request_counter) != NGX_OK) { + return NGX_CONF_ERROR; + } + +#endif + return rv; } @@ -2414,6 +2473,11 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; ngx_http_core_loc_conf_t *clcf, *pclcf; +#if (NGX_STATUS) + ngx_http_core_srv_conf_t *cscf; + u_char *p_name; + u_char *p_caption; +#endif ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { @@ -2580,6 +2644,73 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) } } +#if (NGX_STATUS) + cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); + + if (cscf->request_counter) { + + clcf->request_counter = + ngx_pcalloc(cf->pool, sizeof(ngx_status_counter_t)); + if (clcf->request_counter == NULL) { + return NGX_CONF_ERROR; + } + + clcf->request_counter->name.len = sizeof("request_host__location_") - 1 + value[1].len; + clcf->request_counter->caption.len = sizeof("Location ") - 1 + value[1].len; + + if (cscf->server_name.len) { + clcf->request_counter->name.len += cscf->server_name.len; + } else { + clcf->request_counter->name.len += sizeof("default") - 1; + } + + if (cf->args->nelts == 3) { + clcf->request_counter->name.len += sizeof("_") - 1 + value[2].len; + clcf->request_counter->caption.len += sizeof(" ") - 1 + value[2].len; + } + + clcf->request_counter->name.data = + ngx_palloc(cf->pool, clcf->request_counter->name.len); + if (clcf->request_counter->name.data == NULL) { + return NGX_CONF_ERROR; + } + + clcf->request_counter->caption.data = + ngx_palloc(cf->pool, clcf->request_counter->caption.len); + if (clcf->request_counter->caption.data == NULL) { + return NGX_CONF_ERROR; + } + + p_name = clcf->request_counter->name.data; + p_caption = clcf->request_counter->caption.data; + + p_name = ngx_sprintf(p_name, "request_host_"); + + if (cscf->server_name.len) { + p_name = ngx_sprintf(p_name, "%V", &cscf->server_name); + } else { + p_name = ngx_sprintf(p_name, "default"); + } + + p_name = ngx_sprintf(p_name, "_location_"); + p_caption = ngx_sprintf(p_caption, "Location "); + + if (cf->args->nelts == 3) { + p_name = ngx_sprintf(p_name, "%V_%V", &value[1], &value[2]); + p_caption = ngx_sprintf(p_caption, "%V %V", &value[1], &value[2]); + } else { + p_name = ngx_sprintf(p_name, "%V", &value[1]); + p_caption = ngx_sprintf(p_caption, "%V", &value[1]); + } + + if (ngx_status_add_child(cf->cycle, cscf->request_counter, + clcf->request_counter) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + +#endif + if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -2946,6 +3077,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) * lcf->auto_redirect = 0; * lcf->alias = 0; * lcf->gzip_proxied = 0; + * lcf->request_counter = NULL; */ lcf->client_max_body_size = NGX_CONF_UNSET; @@ -3243,6 +3375,14 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) #endif +#if (NGX_STATUS) + if (prev->request_counter) { + + conf->request_counter = prev->request_counter; + + } +#endif + return NGX_CONF_OK; } diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 2c4e45a..cfe2e64 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -168,6 +168,10 @@ typedef struct { ngx_flag_t underscores_in_headers; ngx_http_core_loc_conf_t **named_locations; + +#if (NGX_STATUS) + ngx_status_counter_t *request_counter; +#endif } ngx_http_core_srv_conf_t; @@ -384,6 +388,10 @@ struct ngx_http_core_loc_conf_s { ngx_queue_t *locations; +#if (NGX_STATUS) + ngx_status_counter_t *request_counter; +#endif + #if 0 ngx_http_core_loc_conf_t *prev_location; #endif diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 32f120c..ccc4d82 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -197,7 +197,7 @@ ngx_http_init_connection(ngx_connection_t *c) rev->handler = ngx_http_init_request; c->write->handler = ngx_http_empty_handler; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif @@ -216,7 +216,7 @@ ngx_http_init_connection(ngx_connection_t *c) ngx_add_timer(rev, c->listening->post_accept_timeout); if (ngx_handle_read_event(rev, 0) != NGX_OK) { -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif ngx_http_close_connection(c); @@ -246,7 +246,7 @@ ngx_http_init_request(ngx_event_t *rev) ngx_http_in6_addr_t *addr6; #endif -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif @@ -494,12 +494,17 @@ ngx_http_init_request(ngx_event_t *rev) ctx->current_request = r; r->log_handler = ngx_http_log_error_handler; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif +#if (NGX_STATUS) + ngx_status_fetch_add(cscf->request_counter); +#endif + + rev->handler(rev); } @@ -1541,7 +1546,7 @@ ngx_http_process_request(ngx_http_request_t *r) ngx_del_timer(c->read); } -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; ngx_atomic_fetch_add(ngx_stat_writing, 1); @@ -2308,7 +2313,7 @@ ngx_http_set_keepalive(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif @@ -2538,7 +2543,7 @@ ngx_http_keepalive_handler(ngx_event_t *rev) b->last += n; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif @@ -2765,7 +2770,7 @@ ngx_http_request_done(ngx_http_request_t *r, ngx_int_t error) } } -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) if (r->stat_reading) { ngx_atomic_fetch_add(ngx_stat_reading, -1); @@ -2852,7 +2857,7 @@ ngx_http_close_connection(ngx_connection_t *c) #endif -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 4fef7c3..0d62cf9 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -496,7 +496,7 @@ struct ngx_http_request_s { unsigned filter_need_temporary:1; unsigned allow_ranges:1; -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) unsigned stat_reading:1; unsigned stat_writing:1; #endif diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c index 44ebded..a44a103 100644 --- a/src/mail/ngx_mail_handler.c +++ b/src/mail/ngx_mail_handler.c @@ -673,7 +673,7 @@ ngx_mail_close_connection(ngx_connection_t *c) #endif -#if (NGX_STAT_STUB) +#if (NGX_STAT_STUB) || (NGX_STATUS) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif