Idea for a statistics module

Tommie Gannert tommie at spotify.com
Thu Oct 8 18:45:35 MSD 2009


Hi!

We felt the stub status module was a bit too non-modular for our
internal services
and created a statistics module where each module can allocate their
stat counters
more dynamically.

It does have a hardcoded limit of around 64 counters currently, and
any suggestion
on how to make it dynamical is highly appreciated.

Apart from adding another "increment the counter", it also requires
the event and core
modules to allocate the counters, why they have been given module init
functions.

You use it by adding

  location /server-status {
    statistics on;
  }

and requesting

  http://example.com/sever-status

for RFC822 formatted output or

  http://example.com/sever-status?type=text/html

for more human friendly text.

The module adds the ability to use histograms of fixed bucket counts,
but note that
the counters will never be reset, so it is nowhere near a snapshot
value. For that we use
a monitoring/graphing system fed with the RFC822 output. This is also
the reason why the
default MIME type is text/plain, rather than text/html. The histograms
are quite useful for plotting
file sizes or request delays.

Hope you find it useful. The original code contained in the patch is
MIT licensed.
Documentation of the code... in the future.

-- 
Tommie
-------------- next part --------------
diff -Nru nginx-0.7.62/auto/modules nginx-0.7.62-statistics/auto/modules
--- nginx-0.7.62/auto/modules	2009-05-01 20:44:50.000000000 +0200
+++ nginx-0.7.62-statistics/auto/modules	2009-10-08 16:09:01.000000000 +0200
@@ -325,6 +325,10 @@
     HTTP_SRCS="$HTTP_SRCS src/http/modules/ngx_http_stub_status_module.c"
 fi
 
+have=NGX_STATISTICS . auto/have
+HTTP_MODULES="$HTTP_MODULES ngx_http_statistics_module"
+HTTP_SRCS="$HTTP_SRCS src/http/modules/ngx_http_statistics_module.c"
+
 #if [ -r $NGX_OBJS/auto ]; then
 #    . $NGX_OBJS/auto
 #fi
diff -Nru nginx-0.7.62/auto/sources nginx-0.7.62-statistics/auto/sources
--- nginx-0.7.62/auto/sources	2009-05-06 16:53:54.000000000 +0200
+++ nginx-0.7.62-statistics/auto/sources	2009-10-08 16:09:01.000000000 +0200
@@ -2,7 +2,7 @@
 # Copyright (C) Igor Sysoev
 
 
-CORE_MODULES="ngx_core_module ngx_errlog_module ngx_conf_module"
+CORE_MODULES="ngx_core_module ngx_errlog_module ngx_conf_module ngx_statistics_module"
 
 CORE_INCS="src/core"
 
@@ -31,6 +31,7 @@
            src/core/ngx_shmtx.h \
            src/core/ngx_connection.h \
            src/core/ngx_cycle.h \
+           src/core/ngx_statistics.h \
            src/core/ngx_conf_file.h \
            src/core/ngx_resolver.h \
            src/core/ngx_open_file_cache.h"
@@ -58,6 +59,7 @@
            src/core/ngx_connection.c \
            src/core/ngx_cycle.c \
            src/core/ngx_spinlock.c \
+           src/core/ngx_statistics.c \
            src/core/ngx_cpuinfo.c \
            src/core/ngx_conf_file.c \
            src/core/ngx_resolver.c \
diff -Nru nginx-0.7.62/src/core/ngx_statistics.c nginx-0.7.62-statistics/src/core/ngx_statistics.c
--- nginx-0.7.62/src/core/ngx_statistics.c	1970-01-01 01:00:00.000000000 +0100
+++ nginx-0.7.62-statistics/src/core/ngx_statistics.c	2009-10-08 16:09:01.000000000 +0200
@@ -0,0 +1,375 @@
+/*
+ * @todo implement the histogram stuff.
+ */
+#include <ngx_statistics.h>
+
+
+/* --- Macros --- */
+#define ngx_statistics_lock_entry(e)    ngx_spinlock(&(e), ngx_pid, 1024)
+#define ngx_statistics_unlock_entry(e)  (void) ngx_atomic_cmp_set(&(e), ngx_pid, 0)
+#define cacheline_size                  128
+
+
+/* --- Types --- */
+typedef struct {
+	const ngx_statistics_descr_t *descr;
+	size_t size;
+	ngx_statistics_type_t type;
+	ngx_atomic_t lock; // Used for non-atomic types
+
+	union {
+		ngx_atomic_uint_t i;
+		ngx_str_t s;
+		ngx_atomic_uint_t *histo;
+	} value;
+
+	u_char data[0];
+} ngx_statistics_entry_t;
+
+typedef struct {
+	ngx_atomic_t create_lock; // Only used for creation
+	ngx_statistics_entry_t *end; // End of entries list
+	ngx_statistics_entry_t entries[0];
+} ngx_statistics_t;
+
+
+
+/* --- Data --- */
+static const ngx_int_t M[] = { 1, 2, 5 };
+static const ngx_int_t BASE = 10;
+
+static ngx_shm_t shm;
+static ngx_statistics_t *ngx_statistics;
+
+
+
+void ngx_statistics_add_int(ngx_stat_key_t key, ngx_int_t delta)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) (ngx_statistics + 1)) + key);
+	ngx_int_t bucket;
+
+	if (!ngx_statistics || key == NGX_STAT_KEY_INVALID || entry >= ngx_statistics->end)
+		return;
+
+	if (entry->type != ngx_statistics_int)
+		return;
+
+	switch (entry->descr->histo) {
+	case ngx_statistics_invalid_histo:
+		ngx_atomic_fetch_add(&entry->value.i, delta);
+		return;
+
+	case ngx_statistics_linear:
+		if (delta < entry->descr->min) bucket = 0;
+		else if (delta >= entry->descr->max) bucket = entry->descr->num_buckets - 1;
+		else bucket = (delta - entry->descr->min) * entry->descr->num_buckets / (entry->descr->max - entry->descr->min);
+		break;
+
+	case ngx_statistics_exponential:
+		{
+			ngx_int_t k = 0;
+			ngx_int_t p = 1;
+
+			for (bucket = 0; M[k] * p < delta - entry->descr->min && bucket < entry->descr->num_buckets - 1; ++bucket) {
+				if (++k == sizeof(M) / sizeof(*M)) {
+					k = 0;
+					p *= BASE;
+				}
+			}
+		}
+
+		break;
+
+	default:
+		return;
+	}
+
+	ngx_atomic_fetch_add(&entry->value.histo[bucket], 1);
+}
+
+ngx_int_t ngx_statistics_get_int(ngx_stat_key_t key, ngx_int_t *value)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) (ngx_statistics + 1)) + key);
+
+	if (!ngx_statistics || key == NGX_STAT_KEY_INVALID || entry >= ngx_statistics->end)
+		return NGX_ERROR;
+
+	if (entry->type != ngx_statistics_int)
+		return NGX_ERROR;
+
+	if (entry->descr->histo != ngx_statistics_invalid_histo)
+		return NGX_ERROR;
+
+	*value = entry->value.i;
+	return NGX_OK;
+}
+
+void ngx_statistics_set_int(ngx_stat_key_t key, ngx_int_t value)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) (ngx_statistics + 1)) + key);
+
+	if (!ngx_statistics || key == NGX_STAT_KEY_INVALID || entry >= ngx_statistics->end)
+		return;
+
+	if (entry->type != ngx_statistics_int)
+		return;
+
+	switch (entry->descr->histo) {
+	case ngx_statistics_invalid_histo:
+		ngx_atomic_cmp_set(&entry->value.i, entry->value.i, value);
+		break;
+
+	case ngx_statistics_linear:
+	case ngx_statistics_exponential:
+		// For histograms, set is the same as add.
+		ngx_statistics_add_int(key, value);
+		break;
+	}
+}
+
+void ngx_statistics_set_str(ngx_stat_key_t key, ngx_str_t *value)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) (ngx_statistics + 1)) + key);
+
+	if (!ngx_statistics || key == NGX_STAT_KEY_INVALID || entry >= ngx_statistics->end)
+		return;
+
+	if (entry->type != ngx_statistics_str)
+		return;
+
+	ngx_statistics_lock_entry(entry->lock);
+
+	if (value->len <= entry->size - sizeof(*entry))
+		entry->value.s.len = value->len;
+	else
+		entry->value.s.len = entry->size - sizeof(*entry);
+
+	ngx_memcpy(entry->value.s.data, value->data, entry->value.s.len);
+
+	ngx_statistics_unlock_entry(entry->lock);
+}
+
+static ngx_stat_key_t ngx_statistics_alloc(const ngx_statistics_descr_t *descr, ngx_statistics_type_t type, size_t size, ngx_statistics_entry_t **entryp)
+{
+	ngx_statistics_entry_t *entry = ngx_statistics->entries;
+
+	if (size < sizeof(*entry))
+		return NGX_STAT_KEY_INVALID;
+
+	while (entry < ngx_statistics->end) {
+		if (descr == entry->descr) {
+			if (type != entry->type || size != entry->size)
+				return NGX_STAT_KEY_INVALID;
+
+			if (entryp) *entryp = entry;
+
+			return (u_char*) ngx_statistics->entries - (u_char*) entry;
+		}
+
+		entry = (ngx_statistics_entry_t*) ((u_char*) entry + entry->size);
+	}
+
+	if (entry != ngx_statistics->end)
+		return NGX_STAT_KEY_INVALID;
+
+	entry->descr = descr;
+	entry->size = size;
+	entry->type = type;
+	entry->lock = 0;
+
+	// entry->value is initialized by _create_*()
+
+	ngx_statistics->end = (ngx_statistics_entry_t*) ((u_char*) ngx_statistics->end + size);
+
+	if (entryp) *entryp = entry;
+
+	return (u_char*) entry - (u_char*) ngx_statistics->entries;
+}
+
+ngx_stat_key_t ngx_statistics_create_int(const ngx_statistics_descr_t *descr, ngx_int_t initial)
+{
+	ngx_stat_key_t ret;
+	ngx_statistics_entry_t *entry;
+	ngx_int_t i;
+
+	if (!ngx_statistics)
+		return NGX_STAT_KEY_INVALID;
+
+	if (descr->histo == ngx_statistics_invalid_histo && descr->num_buckets != 1)
+		return NGX_STAT_KEY_INVALID;
+
+	ngx_spinlock(&ngx_statistics->create_lock, ngx_pid, 1024);
+
+	ret = ngx_statistics_alloc(descr, ngx_statistics_int, sizeof(ngx_statistics_entry_t) + sizeof(ngx_int_t) * descr->num_buckets, &entry);
+
+	if (ret == NGX_STAT_KEY_INVALID) {
+		(void) ngx_atomic_cmp_set(&ngx_statistics->create_lock, ngx_pid, 0);
+
+		return ret;
+	}
+
+	if (descr->histo != ngx_statistics_invalid_histo) {
+		entry->value.histo = (ngx_atomic_uint_t*) entry->data;
+
+		for (i = 0; i < descr->num_buckets; ++i)
+			entry->value.histo[i] = initial;
+	} else {
+		entry->value.i = initial;
+	}
+
+	(void) ngx_atomic_cmp_set(&ngx_statistics->create_lock, ngx_pid, 0);
+
+	return ret;
+}
+
+ngx_stat_key_t ngx_statistics_create_str(const ngx_statistics_descr_t *descr, ngx_int_t max_size, ngx_str_t *initial)
+{
+	ngx_stat_key_t ret;
+	ngx_statistics_entry_t *entry;
+
+	if (!ngx_statistics)
+		return NGX_STAT_KEY_INVALID;
+
+	if (descr->histo != ngx_statistics_invalid_histo)
+		return NGX_STAT_KEY_INVALID;
+
+	ngx_spinlock(&ngx_statistics->create_lock, ngx_pid, 1024);
+
+	ret = ngx_statistics_alloc(descr, ngx_statistics_str, sizeof(ngx_statistics_entry_t) + max_size + 1, &entry);
+
+	if (ret == NGX_STAT_KEY_INVALID) {
+		(void) ngx_atomic_cmp_set(&ngx_statistics->create_lock, ngx_pid, 0);
+
+		return ret;
+	}
+
+	entry->value.s.len = initial->len;
+	entry->value.s.data = entry->data;
+	ngx_memcpy(entry->value.s.data, initial->data, initial->len);
+
+	(void) ngx_atomic_cmp_set(&ngx_statistics->create_lock, ngx_pid, 0);
+
+	return ret;
+}
+
+ngx_statistics_type_t ngx_statistics_entry(ngx_stat_iter_t it, const ngx_statistics_descr_t **descr, void **value)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) ngx_statistics->entries) + it);
+
+	if (!ngx_statistics_valid(it))
+		return ngx_statistics_invalid_type;
+
+	if (descr) *descr = entry->descr;
+
+	if (value) {
+		if (entry->descr->histo == ngx_statistics_invalid_histo)
+			*value = &entry->value;
+		else
+			*value = entry->value.histo;
+	}
+
+	return entry->type;
+}
+
+ngx_stat_iter_t ngx_statistics_iter(void)
+{
+	return 0;
+}
+
+ngx_stat_iter_t ngx_statistics_next(ngx_stat_iter_t it)
+{
+	ngx_statistics_entry_t *entry = (void*) (((u_char*) ngx_statistics->entries) + it);
+
+	if (!ngx_statistics_valid(it))
+		return it;
+
+	return it + entry->size;
+}
+
+ngx_int_t ngx_statistics_valid(ngx_stat_iter_t it)
+{
+	return ngx_statistics && (u_char*) ngx_statistics->entries + it < (u_char*) ngx_statistics->end;
+}
+
+static ngx_int_t powi(ngx_int_t base, ngx_int_t exp)
+{
+	ngx_int_t ret = 1;
+
+	while (exp--) ret *= base;
+
+	return ret;
+}
+
+ngx_int_t ngx_statistics_bucket_lower(const ngx_statistics_descr_t *descr, ngx_int_t bucket)
+{
+	switch (descr->histo) {
+	case ngx_statistics_invalid_histo:
+		return descr->min;
+
+	case ngx_statistics_linear:
+		return descr->min + (descr->max - descr->min) * bucket / descr->num_buckets;
+
+	case ngx_statistics_exponential:
+		if (!bucket) return descr->min;
+
+		--bucket;
+
+		return descr->min + M[bucket % (sizeof(M) / sizeof(*M))] * powi(BASE, bucket / 3);
+	}
+
+	return -1;
+}
+
+
+static ngx_int_t ngx_statistics_module_init(ngx_cycle_t *cycle)
+{
+	/* This would fit around 64 entries */
+	shm.size = 64 * cacheline_size;
+	shm.log = cycle->log;
+	shm.name.len = sizeof("ngx_statistics");
+	shm.name.data = (u_char*) "ngx_statistics";
+
+	if (ngx_shm_alloc(&shm) != NGX_OK) {
+		return NGX_ERROR;
+	}
+
+	ngx_statistics = (ngx_statistics_t*) shm.addr;
+
+	ngx_statistics->create_lock = 0;
+	ngx_statistics->end = ngx_statistics->entries;
+
+	return NGX_OK;
+}
+
+static void ngx_statistics_module_exit_process(ngx_cycle_t *cycle)
+{
+	ngx_shm_free(&shm);
+	ngx_statistics = NULL;
+}
+
+static void ngx_statistics_module_exit(ngx_cycle_t *cycle)
+{
+	ngx_statistics_module_exit_process(cycle);
+}
+
+
+static ngx_core_module_t ngx_statistics_module_ctx = {
+	ngx_string("statistics"),
+	NULL,
+	NULL
+};
+
+ngx_module_t  ngx_statistics_module = {
+	NGX_MODULE_V1,
+	&ngx_statistics_module_ctx,            /* module context */
+	NULL,                                  /* module directives */
+	NGX_CORE_MODULE,                       /* module type */
+	NULL,                                  /* init master */
+	ngx_statistics_module_init,            /* init module */
+	NULL,                                  /* init process */
+	NULL,                                  /* init thread */
+	NULL,                                  /* exit thread */
+	ngx_statistics_module_exit_process,    /* exit process */
+	ngx_statistics_module_exit,            /* exit master */
+	NGX_MODULE_V1_PADDING
+};
diff -Nru nginx-0.7.62/src/core/ngx_statistics.h nginx-0.7.62-statistics/src/core/ngx_statistics.h
--- nginx-0.7.62/src/core/ngx_statistics.h	1970-01-01 01:00:00.000000000 +0100
+++ nginx-0.7.62-statistics/src/core/ngx_statistics.h	2009-10-08 16:09:01.000000000 +0200
@@ -0,0 +1,61 @@
+#ifndef _NGX_STATISTICS_H_INCLUDED_
+#define _NGX_STATISTICS_H_INCLUDED_
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+/* --- Macros --- */
+#define NGX_STAT_KEY_INVALID ((ngx_stat_key_t) -1)
+
+
+/* --- Types --- */
+typedef ngx_int_t ngx_stat_key_t;
+typedef size_t ngx_stat_iter_t;
+
+typedef enum {
+	ngx_statistics_invalid_type = 0,
+	ngx_statistics_int,
+	ngx_statistics_str
+} ngx_statistics_type_t;
+
+typedef enum {
+	ngx_statistics_invalid_histo = 0,
+	ngx_statistics_linear,
+	ngx_statistics_exponential
+} ngx_statistics_histo_t;
+
+typedef struct {
+	ngx_str_t label;
+	ngx_str_t name;
+	ngx_str_t unit;
+	float scale;
+	int num_decimals;
+	ngx_int_t min;
+	ngx_int_t max;
+	ngx_int_t num_buckets;
+	ngx_statistics_histo_t histo;
+} ngx_statistics_descr_t;
+
+
+/* --- Functions --- */
+// Creators
+ngx_stat_key_t ngx_statistics_create_int(const ngx_statistics_descr_t *descr, ngx_int_t initial);
+ngx_stat_key_t ngx_statistics_create_str(const ngx_statistics_descr_t *descr, ngx_int_t max_size, ngx_str_t *initial);
+
+// Accessors
+ngx_int_t ngx_statistics_get_int(ngx_stat_key_t key, ngx_int_t *value);
+
+// Mutators
+void ngx_statistics_add_int(ngx_stat_key_t key, ngx_int_t delta);
+void ngx_statistics_set_int(ngx_stat_key_t key, ngx_int_t value);
+void ngx_statistics_set_str(ngx_stat_key_t key, ngx_str_t *value);
+
+// Iterators
+ngx_statistics_type_t ngx_statistics_entry(ngx_stat_iter_t it, const ngx_statistics_descr_t **descr, void **value);
+ngx_stat_iter_t ngx_statistics_iter(void);
+ngx_stat_iter_t ngx_statistics_next(ngx_stat_iter_t it);
+ngx_int_t ngx_statistics_valid(ngx_stat_iter_t it);
+
+ngx_int_t ngx_statistics_bucket_lower(const ngx_statistics_descr_t *descr, ngx_int_t bucket);
+
+#endif /* _NGX_STATISTICS_H_INCLUDED_ */
diff -Nru nginx-0.7.62/src/core/ngx_string.c nginx-0.7.62-statistics/src/core/ngx_string.c
--- nginx-0.7.62/src/core/ngx_string.c	2009-09-07 13:31:20.000000000 +0200
+++ nginx-0.7.62-statistics/src/core/ngx_string.c	2009-10-08 16:09:01.000000000 +0200
@@ -201,8 +201,13 @@
                 case '.':
                     fmt++;
 
-                    while (*fmt >= '0' && *fmt <= '9') {
-                        frac_width = frac_width * 10 + *fmt++ - '0';
+                    if (*fmt == '*') {
+                        frac_width = va_arg(args, size_t);
+                        fmt++;
+                    } else {
+                        while (*fmt >= '0' && *fmt <= '9') {
+                            frac_width = frac_width * 10 + *fmt++ - '0';
+                        }
                     }
 
                     break;
diff -Nru nginx-0.7.62/src/event/ngx_event.c nginx-0.7.62-statistics/src/event/ngx_event.c
--- nginx-0.7.62/src/event/ngx_event.c	2009-04-28 22:03:59.000000000 +0200
+++ nginx-0.7.62-statistics/src/event/ngx_event.c	2009-10-08 16:09:01.000000000 +0200
@@ -74,6 +74,12 @@
 
 #endif
 
+#if (NGX_STATISTICS)
+ngx_stat_key_t ngx_stat_accepted_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_active_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_handled_key = NGX_STAT_KEY_INVALID;
+#endif
+
 
 
 static ngx_command_t  ngx_events_commands[] = {
@@ -537,6 +543,35 @@
 
 #endif
 
+#if (NGX_STATISTICS)
+    {
+	static const ngx_statistics_descr_t ACCEPTED = {
+		ngx_string("Event-Accepted"),
+		ngx_string("Accepted Connections"),
+		ngx_string("connections"),
+		1.0f, 0,
+		0, 0, 1, ngx_statistics_invalid_histo };
+
+	static const ngx_statistics_descr_t ACTIVE = {
+		ngx_string("Event-Active"),
+		ngx_string("Active Connections"),
+		ngx_string("connections"),
+		1.0f, 0,
+		0, 0, 1, ngx_statistics_invalid_histo };
+
+	static const ngx_statistics_descr_t HANDLED = {
+		ngx_string("Event-Handled"),
+		ngx_string("Handled Connections"),
+		ngx_string("connections"),
+		1.0f, 0,
+		0, 0, 1, ngx_statistics_invalid_histo };
+
+        ngx_stat_accepted_key = ngx_statistics_create_int(&ACCEPTED, 0);
+        ngx_stat_active_key = ngx_statistics_create_int(&ACTIVE, 0);
+        ngx_stat_handled_key = ngx_statistics_create_int(&HANDLED, 0);
+    }
+#endif
+
     (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
 
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
diff -Nru nginx-0.7.62/src/event/ngx_event.h nginx-0.7.62-statistics/src/event/ngx_event.h
--- nginx-0.7.62/src/event/ngx_event.h	2008-02-28 21:31:33.000000000 +0100
+++ nginx-0.7.62-statistics/src/event/ngx_event.h	2009-10-08 16:09:01.000000000 +0200
@@ -10,6 +10,7 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_statistics.h>
 
 
 #define NGX_INVALID_INDEX  0xd0d0d0d0
@@ -488,6 +489,12 @@
 
 #endif
 
+#if (NGX_STATISTICS)
+extern ngx_stat_key_t ngx_stat_accepted_key;
+extern ngx_stat_key_t ngx_stat_active_key;
+extern ngx_stat_key_t ngx_stat_handled_key;
+#endif
+
 
 #define NGX_UPDATE_TIME         1
 #define NGX_POST_EVENTS         2
diff -Nru nginx-0.7.62/src/event/ngx_event_accept.c nginx-0.7.62-statistics/src/event/ngx_event_accept.c
--- nginx-0.7.62/src/event/ngx_event_accept.c	2009-06-22 11:31:33.000000000 +0200
+++ nginx-0.7.62-statistics/src/event/ngx_event_accept.c	2009-10-08 16:09:01.000000000 +0200
@@ -77,6 +77,9 @@
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
 #endif
+#if (NGX_STATISTICS)
+        (void) ngx_statistics_add_int(ngx_stat_accepted_key, 1);
+#endif
 
         ngx_accept_disabled = ngx_cycle->connection_n / 8
                               - ngx_cycle->free_connection_n;
@@ -95,6 +98,9 @@
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
 #endif
+#if (NGX_STATISTICS)
+        (void) ngx_statistics_add_int(ngx_stat_active_key, 1);
+#endif
 
         c->pool = ngx_create_pool(ls->pool_size, ev->log);
         if (c->pool == NULL) {
@@ -190,6 +196,9 @@
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
 #endif
+#if (NGX_STATISTICS)
+        (void) ngx_statistics_add_int(ngx_stat_handled_key, 1);
+#endif
 
 #if (NGX_THREADS)
         rev->lock = &c->lock;
@@ -381,6 +390,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_active_key, 1);
+#endif
 }
 
 
diff -Nru nginx-0.7.62/src/http/modules/ngx_http_statistics_module.c nginx-0.7.62-statistics/src/http/modules/ngx_http_statistics_module.c
--- nginx-0.7.62/src/http/modules/ngx_http_statistics_module.c	1970-01-01 01:00:00.000000000 +0100
+++ nginx-0.7.62-statistics/src/http/modules/ngx_http_statistics_module.c	2009-10-08 16:09:01.000000000 +0200
@@ -0,0 +1,315 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static char *ngx_http_set_statistics(ngx_conf_t *cf, ngx_command_t *cmd,
+                                     void *conf);
+
+static ngx_command_t  ngx_http_statistics_commands[] = {
+
+    { ngx_string("statistics"),
+      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_http_set_statistics,
+      0,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+
+static ngx_http_module_t  ngx_http_statistics_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    NULL,                                  /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    NULL,                                  /* create location configuration */
+    NULL                                   /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_statistics_module = {
+    NGX_MODULE_V1,
+    &ngx_http_statistics_module_ctx,       /* module context */
+    ngx_http_statistics_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_buf_t* ngx_http_statistics_html_handler(ngx_http_request_t *r)
+{
+	static ngx_str_t HEADER = ngx_string(
+		"<html>\r\n"
+		"\t<head>\r\n"
+		"\t\t<title>Nginx Statistics</title>\r\n"
+		"\t\t<style>\r\n"
+		"\t\t\t.int { text-align: right; }\r\n"
+		"\t\t\tTH { text-align: left; }\r\n"
+		"\t\t\tTABLE.histogram { width: 100%; }\r\n"
+		"\t\t</style>\r\n"
+		"\t</head>\r\n"
+		"\t<body>\r\n"
+		"\t\t<h1>Nginx Statistics</h1>\r\n"
+		"\t\t<table class=\"stats\" border=\"1\" cellspacing=\"0\">\r\n");
+
+	static ngx_str_t FOOTER = ngx_string(
+		"\t\t</table>\r\n"
+		"\t</body>\r\n"
+		"</html>\r\n");
+
+	ngx_buf_t *b;
+	size_t size = HEADER.len + FOOTER.len;
+	ngx_stat_iter_t it;
+
+	for (it = ngx_statistics_iter(); ngx_statistics_valid(it); it = ngx_statistics_next(it)) {
+		const ngx_statistics_descr_t *descr;
+		void *value;
+
+		switch (ngx_statistics_entry(it, &descr, &value)) {
+		case ngx_statistics_int:
+			size += 27 + 27 + (160 + NGX_ATOMIC_T_LEN * 3) * descr->num_buckets + 11;
+			break;
+
+		case ngx_statistics_str:
+			size += 13 + ((ngx_str_t*) value)->len;
+			break;
+
+		default:
+			break;
+		}
+
+		size += 11 + descr->name.len + 8 + 22 + descr->unit.len + 12;
+	}
+
+	b = ngx_create_temp_buf(r->pool, size);
+
+	if (b == NULL) return NULL;
+
+	b->last = ngx_sprintf(b->last, "%V", &HEADER);
+
+	for (it = ngx_statistics_iter(); ngx_statistics_valid(it); it = ngx_statistics_next(it)) {
+		const ngx_statistics_descr_t *descr;
+		void *value;
+		ngx_int_t i;
+		ngx_statistics_type_t type = ngx_statistics_entry(it, &descr, &value);
+
+		b->last = ngx_sprintf(b->last, "\t\t\t<tr><th>%V</th><td", &descr->name);
+
+		switch (type) {
+		case ngx_statistics_int:
+			if (descr->histo != ngx_statistics_invalid_histo) {
+				ngx_int_t sum = 0;
+
+				b->last = ngx_sprintf(b->last, " class=\"histo\" width=\"50%%\">");
+
+				for (i = 0; i < descr->num_buckets; ++i)
+					sum += ((ngx_atomic_uint_t*) value)[i];
+
+				if (sum) {
+					b->last = ngx_sprintf(b->last, "<table class=\"histogram\">\r\n");
+
+					for (i = 0; i < descr->num_buckets; ++i) {
+						b->last = ngx_sprintf(b->last, "\t\t\t\t<tr><th class=\"int\">");
+						if (i + 1 < descr->num_buckets)
+							b->last = ngx_sprintf(b->last, "<%.*f", descr->num_decimals, descr->scale * ngx_statistics_bucket_lower(descr, i + 1));
+						else
+							b->last = ngx_sprintf(b->last, "More");
+						b->last = ngx_sprintf(b->last, "</th><td width=\"100%%\"><div style=\"float:left;background:black;width:%i%%\">&nbsp;</div></td><td class=\"int\">%i</td></tr>\r\n", 100 * ((ngx_atomic_uint_t*) value)[i] / sum, ((ngx_atomic_uint_t*) value)[i]);
+					}
+
+					b->last = ngx_sprintf(b->last, "\t\t\t</table>");
+				}
+			} else {
+				b->last = ngx_sprintf(b->last, " class=\"int\">%.*f", descr->num_decimals, descr->scale * *(ngx_atomic_uint_t*) value);
+			}
+
+
+			break;
+
+		case ngx_statistics_str:
+			b->last = ngx_sprintf(b->last, " class=\"str\">%V", (ngx_str_t*) value);
+			break;
+
+		default:
+			break;
+		}
+
+		b->last = ngx_sprintf(b->last, "</td><td class=\"unit\">%V</td></tr>\r\n", &descr->unit);
+	}
+
+	b->last = ngx_sprintf(b->last, "%V", &FOOTER);
+
+	return b;
+}
+
+
+static ngx_buf_t* ngx_http_statistics_plain_handler(ngx_http_request_t *r)
+{
+	ngx_buf_t *b;
+	size_t size = 0;
+	ngx_stat_iter_t it;
+
+	for (it = ngx_statistics_iter(); ngx_statistics_valid(it); it = ngx_statistics_next(it)) {
+		const ngx_statistics_descr_t *descr;
+		void *value;
+
+		switch (ngx_statistics_entry(it, &descr, &value)) {
+		case ngx_statistics_int:
+			size += (1 + NGX_ATOMIC_T_LEN + 1 + NGX_ATOMIC_T_LEN + 2) * descr->num_buckets;
+			break;
+
+		case ngx_statistics_str:
+			size += ((ngx_str_t*) value)->len;
+			break;
+
+		default:
+			break;
+		}
+
+		size += descr->label.len + 2 + 2;
+	}
+
+	b = ngx_create_temp_buf(r->pool, size);
+
+	if (b == NULL) return NULL;
+
+	for (it = ngx_statistics_iter(); ngx_statistics_valid(it); it = ngx_statistics_next(it)) {
+		const ngx_statistics_descr_t *descr;
+		void *value;
+		ngx_int_t i;
+		ngx_statistics_type_t type = ngx_statistics_entry(it, &descr, &value);
+
+		b->last = ngx_sprintf(b->last, "%V:", &descr->label);
+
+		switch (type) {
+		case ngx_statistics_int:
+			if (descr->histo != ngx_statistics_invalid_histo) {
+				b->last = ngx_sprintf(b->last, "\r\n");
+
+				for (i = 0; i < descr->num_buckets; ++i) {
+					*b->last++ = '\t';
+					if (i > 0)
+						b->last = ngx_sprintf(b->last, "%i", ngx_statistics_bucket_lower(descr, i));
+					*b->last++ = '-';
+					if (i + 1 < descr->num_buckets)
+						b->last = ngx_sprintf(b->last, "%i", ngx_statistics_bucket_lower(descr, i + 1) - 1);
+					b->last = ngx_sprintf(b->last, "=%i\r\n", ((ngx_atomic_uint_t*) value)[i]);
+				}
+			} else {
+				b->last = ngx_sprintf(b->last, " %i\r\n", *(ngx_atomic_uint_t*) value);
+			}
+
+
+			break;
+
+		case ngx_statistics_str:
+			b->last = ngx_sprintf(b->last, " %V\r\n", (ngx_str_t*) value);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return b;
+}
+
+
+static ngx_int_t ngx_http_statistics_handler(ngx_http_request_t *r)
+{
+    ngx_int_t   rc;
+    ngx_buf_t   *b;
+    ngx_chain_t out;
+    ngx_str_t   content_type = ngx_string("text/plain");
+    ngx_buf_t*  (*handler)(ngx_http_request_t*) = ngx_http_statistics_plain_handler;
+
+    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;
+    }
+
+    // XXX: Handle Accept in addition to ?type=
+    if (r->args.len > sizeof("type=") - 1 && !ngx_memcmp(r->args.data, "type=", sizeof("type=") - 1)) {
+        ngx_str_t TEXT_PLAIN = ngx_string("text/plain");
+        ngx_str_t TEXT_HTML = ngx_string("text/html");
+
+        if (r->args.len - (sizeof("type=") - 1) == TEXT_PLAIN.len && !ngx_memcmp(r->args.data + (sizeof("type=") - 1), TEXT_PLAIN.data, TEXT_PLAIN.len)) {
+		content_type = TEXT_PLAIN;
+		handler = ngx_http_statistics_plain_handler;
+	} else if (r->args.len - (sizeof("type=") - 1) == TEXT_HTML.len && !ngx_memcmp(r->args.data + (sizeof("type=") - 1), TEXT_HTML.data, TEXT_HTML.len)) {
+		content_type = TEXT_HTML;
+		handler = ngx_http_statistics_html_handler;
+	}
+    }
+
+    r->headers_out.content_type = content_type;
+
+    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;
+        }
+    }
+
+    b = handler(r);
+
+    if (b == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    out.buf = b;
+    out.next = NULL;
+
+    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 char *ngx_http_set_statistics(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t  *clcf;
+
+    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+    clcf->handler = ngx_http_statistics_handler;
+
+    return NGX_CONF_OK;
+}
diff -Nru nginx-0.7.62/src/http/ngx_http_core_module.c nginx-0.7.62-statistics/src/http/ngx_http_core_module.c
--- nginx-0.7.62/src/http/ngx_http_core_module.c	2009-09-07 12:01:26.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_core_module.c	2009-10-08 16:09:01.000000000 +0200
@@ -20,6 +20,8 @@
 #define NGX_HTTP_REQUEST_BODY_FILE_CLEAN  2
 
 
+static ngx_int_t ngx_http_core_module_init(ngx_cycle_t *cycle);
+
 static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);
 static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r,
     ngx_http_location_tree_node_t *node);
@@ -153,6 +155,15 @@
 
 #endif
 
+#if (NGX_STATISTICS)
+ngx_stat_key_t ngx_stat_http_reading_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_writing_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_requests_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_request_time_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_active_requests_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_writing_file_key = NGX_STAT_KEY_INVALID;
+#endif
+
 
 static ngx_command_t  ngx_http_core_commands[] = {
 
@@ -695,7 +706,7 @@
     ngx_http_core_commands,                /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     NULL,                                  /* init master */
-    NULL,                                  /* init module */
+    ngx_http_core_module_init,             /* init module */
     NULL,                                  /* init process */
     NULL,                                  /* init thread */
     NULL,                                  /* exit thread */
@@ -708,6 +719,65 @@
 ngx_str_t  ngx_http_core_get_method = { 3, (u_char *) "GET " };
 
 
+static ngx_int_t
+ngx_http_core_module_init(ngx_cycle_t *cycle)
+{
+#if (NGX_STATISTICS)
+    static const ngx_statistics_descr_t READING = {
+        ngx_string("Http-Requests-Reading"),
+        ngx_string("HTTP Requests Currently Reading"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t WRITING = {
+        ngx_string("Http-Requests-Writing"),
+        ngx_string("HTTP Requests Currently Writing"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t REQUESTS = {
+        ngx_string("Http-Requests-Total"),
+        ngx_string("Processed HTTP Requests Since Start"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t REQUEST_TIME = {
+        ngx_string("Http-Time-Request"),
+        ngx_string("Total Time of HTTP Request"),
+        ngx_string("s"),
+        0.001f, 3,
+        0, 0, 15, ngx_statistics_exponential };
+
+    static const ngx_statistics_descr_t ACTIVE_REQUESTS = {
+        ngx_string("Http-Requests-Active"),
+        ngx_string("HTTP Requests Currently Active"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t WRITING_FILE = {
+        ngx_string("Http-Requests-Writing-File"),
+        ngx_string("HTTP Requests Currently Writing File"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+
+    ngx_stat_http_reading_key = ngx_statistics_create_int(&READING, 0);
+    ngx_stat_http_writing_key = ngx_statistics_create_int(&WRITING, 0);
+    ngx_stat_http_requests_key = ngx_statistics_create_int(&REQUESTS, 0);
+    ngx_stat_http_request_time_key = ngx_statistics_create_int(&REQUEST_TIME, 0);
+    ngx_stat_http_active_requests_key = ngx_statistics_create_int(&ACTIVE_REQUESTS, 0);
+    ngx_stat_http_writing_file_key = ngx_statistics_create_int(&WRITING_FILE, 0);
+#endif
+
+    return NGX_OK;
+}
+
+
 void
 ngx_http_handler(ngx_http_request_t *r)
 {
diff -Nru nginx-0.7.62/src/http/ngx_http_core_module.h nginx-0.7.62-statistics/src/http/ngx_http_core_module.h
--- nginx-0.7.62/src/http/ngx_http_core_module.h	2009-09-07 12:01:26.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_core_module.h	2009-10-08 16:09:01.000000000 +0200
@@ -472,6 +472,15 @@
 
 extern ngx_str_t  ngx_http_core_get_method;
 
+#if (NGX_STATISTICS)
+extern ngx_stat_key_t ngx_stat_http_reading_key;
+extern ngx_stat_key_t ngx_stat_http_writing_key;
+extern ngx_stat_key_t ngx_stat_http_requests_key;
+extern ngx_stat_key_t ngx_stat_http_request_time_key;
+extern ngx_stat_key_t ngx_stat_http_active_requests_key;
+extern ngx_stat_key_t ngx_stat_http_writing_file_key;
+#endif
+
 
 #define ngx_http_clear_content_length(r)                                      \
                                                                               \
diff -Nru nginx-0.7.62/src/http/ngx_http_request.c nginx-0.7.62-statistics/src/http/ngx_http_request.c
--- nginx-0.7.62/src/http/ngx_http_request.c	2009-09-07 13:11:24.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_request.c	2009-10-08 16:09:01.000000000 +0200
@@ -200,6 +200,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, 1);
+#endif
 
     if (rev->ready) {
         /* the deferred accept(), rtsig, aio, iocp */
@@ -219,6 +222,9 @@
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, -1);
+#endif
         ngx_http_close_connection(c);
         return;
     }
@@ -249,6 +255,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, -1);
+#endif
 
     c = rev->data;
 
@@ -501,6 +510,12 @@
     r->stat_reading = 1;
     (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, 1);
+    r->stat_reading = 1;
+    (void) ngx_statistics_add_int(ngx_stat_http_requests_key, 1);
+    (void) ngx_statistics_add_int(ngx_stat_http_active_requests_key, 1);
+#endif
 
     rev->handler(rev);
 }
@@ -1561,6 +1576,12 @@
     (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);
     r->stat_writing = 1;
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, -1);
+    r->stat_reading = 0;
+    (void) ngx_statistics_add_int(ngx_stat_http_writing_key, 1);
+    r->stat_writing = 1;
+#endif
 
     c->read->handler = ngx_http_request_handler;
     c->write->handler = ngx_http_request_handler;
@@ -2330,6 +2351,9 @@
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
+#if (NGX_STATISTICS)
+        (void) ngx_statistics_add_int(ngx_stat_http_reading_key, 1);
+#endif
 
         hc->pipeline = 1;
         c->log->action = "reading client pipelined request line";
@@ -2560,6 +2584,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_reading_key, 1);
+#endif
 
     c->log->handler = ngx_http_log_error;
     c->log->action = "reading client request line";
@@ -2795,6 +2822,16 @@
     }
 
 #endif
+#if (NGX_STATISTICS)
+    if (r->stat_reading)
+        (void) ngx_statistics_add_int(ngx_stat_http_reading_key, -1);
+
+    if (r->stat_writing)
+        (void) ngx_statistics_add_int(ngx_stat_http_writing_key, -1);
+
+    if (r->stat_reading || r->stat_writing)
+        (void) ngx_statistics_add_int(ngx_stat_http_active_requests_key, -1);
+#endif
 
     if (error && r->headers_out.status == 0) {
         r->headers_out.status = error;
@@ -2874,6 +2911,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_active_key, -1);
+#endif
 
     c->destroyed = 1;
 
diff -Nru nginx-0.7.62/src/http/ngx_http_request.h nginx-0.7.62-statistics/src/http/ngx_http_request.h
--- nginx-0.7.62/src/http/ngx_http_request.h	2009-05-22 13:05:26.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_request.h	2009-10-08 16:09:01.000000000 +0200
@@ -497,7 +497,7 @@
     unsigned                          filter_need_temporary:1;
     unsigned                          allow_ranges:1;
 
-#if (NGX_STAT_STUB)
+#if (NGX_STAT_STUB) || (NGX_STATISTICS)
     unsigned                          stat_reading:1;
     unsigned                          stat_writing:1;
 #endif
diff -Nru nginx-0.7.62/src/http/ngx_http_request_body.c nginx-0.7.62-statistics/src/http/ngx_http_request_body.c
--- nginx-0.7.62/src/http/ngx_http_request_body.c	2008-12-26 14:43:42.000000000 +0100
+++ nginx-0.7.62-statistics/src/http/ngx_http_request_body.c	2009-10-08 16:09:01.000000000 +0200
@@ -414,7 +414,13 @@
         rb->temp_file = tf;
     }
 
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_writing_file_key, 1);
+#endif
     n = ngx_write_chain_to_temp_file(rb->temp_file, body);
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_writing_file_key, -1);
+#endif
 
     /* TODO: n == 0 or not complete and level event */
 
diff -Nru nginx-0.7.62/src/http/ngx_http_upstream.c nginx-0.7.62-statistics/src/http/ngx_http_upstream.c
--- nginx-0.7.62/src/http/ngx_http_upstream.c	2009-09-07 13:27:07.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_upstream.c	2009-10-08 16:09:01.000000000 +0200
@@ -18,6 +18,8 @@
     ngx_http_variable_value_t *v, uintptr_t data);
 #endif
 
+static ngx_int_t ngx_http_upstream_module_init(ngx_cycle_t *cycle);
+
 static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
 static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
 static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
@@ -132,6 +134,12 @@
 static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);
 #endif
 
+#if (NGX_STATISTICS)
+ngx_stat_key_t ngx_stat_http_cached_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_upstream_key = NGX_STAT_KEY_INVALID;
+ngx_stat_key_t ngx_stat_http_active_upstream_key = NGX_STAT_KEY_INVALID;
+#endif
+
 
 ngx_http_upstream_header_t  ngx_http_upstream_headers_in[] = {
 
@@ -296,7 +304,7 @@
     ngx_http_upstream_commands,            /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     NULL,                                  /* init master */
-    NULL,                                  /* init module */
+    ngx_http_upstream_module_init,         /* init module */
     NULL,                                  /* init process */
     NULL,                                  /* init thread */
     NULL,                                  /* exit thread */
@@ -354,6 +362,40 @@
 };
 
 
+static ngx_int_t
+ngx_http_upstream_module_init(ngx_cycle_t *cycle)
+{
+#if (NGX_STATISTICS)
+    static const ngx_statistics_descr_t UPSTREAM = {
+        ngx_string("Http-Upstreams-Total"),
+        ngx_string("HTTP Upstream Requests Since Start"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t ACTIVE_UPSTREAM = {
+        ngx_string("Http-Upstreams-Active"),
+        ngx_string("HTTP Upstream Requests Currently Active"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    static const ngx_statistics_descr_t CACHED = {
+        ngx_string("Http-Cached"),
+        ngx_string("HTTP Requests Cached Since Start"),
+        ngx_string("requests"),
+        1.0f, 0,
+        0, 0, 1, ngx_statistics_invalid_histo };
+
+    ngx_stat_http_upstream_key = ngx_statistics_create_int(&UPSTREAM, 0);
+    ngx_stat_http_active_upstream_key = ngx_statistics_create_int(&ACTIVE_UPSTREAM, 0);
+    ngx_stat_http_cached_key = ngx_statistics_create_int(&CACHED, 0);
+#endif
+
+    return NGX_OK;
+}
+
+
 void
 ngx_http_upstream_init(ngx_http_request_t *r)
 {
@@ -977,6 +1019,10 @@
         u->state->response_msec = tp->msec - u->state->response_msec;
     }
 
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_active_upstream_key, 1);
+#endif
+
     u->state = ngx_array_push(r->upstream_states);
     if (u->state == NULL) {
         ngx_http_upstream_finalize_request(r, u,
@@ -1248,6 +1294,12 @@
 
     c->log->action = "sending request to upstream";
 
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_upstream_key, 1);
+    if (u->cacheable)
+        (void) ngx_statistics_add_int(ngx_stat_http_cached_key, 1);
+#endif
+
     rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs);
 
     u->request_sent = 1;
@@ -2804,6 +2856,10 @@
 {
     ngx_time_t  *tp;
 
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_http_active_upstream_key, -1);
+#endif
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "finalize http upstream request: %i", rc);
 
diff -Nru nginx-0.7.62/src/http/ngx_http_upstream.h nginx-0.7.62-statistics/src/http/ngx_http_upstream.h
--- nginx-0.7.62/src/http/ngx_http_upstream.h	2009-09-07 11:49:51.000000000 +0200
+++ nginx-0.7.62-statistics/src/http/ngx_http_upstream.h	2009-10-08 16:09:01.000000000 +0200
@@ -332,6 +332,10 @@
 extern ngx_module_t        ngx_http_upstream_module;
 extern ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[];
 
+#if (NGX_STATISTICS)
+extern ngx_stat_key_t ngx_stat_http_cached_key;
+extern ngx_stat_key_t ngx_stat_http_upstream_key;
+#endif
 
 
 #endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
diff -Nru nginx-0.7.62/src/mail/ngx_mail_handler.c nginx-0.7.62-statistics/src/mail/ngx_mail_handler.c
--- nginx-0.7.62/src/mail/ngx_mail_handler.c	2009-06-22 11:31:33.000000000 +0200
+++ nginx-0.7.62-statistics/src/mail/ngx_mail_handler.c	2009-10-08 16:09:01.000000000 +0200
@@ -712,6 +712,9 @@
 #if (NGX_STAT_STUB)
     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
 #endif
+#if (NGX_STATISTICS)
+    (void) ngx_statistics_add_int(ngx_stat_active_key, -1);
+#endif
 
     c->destroyed = 1;
 


More information about the nginx mailing list