Idea for a statistics module

Tommie Gannert tommie at spotify.com
Fri Oct 9 11:45:53 MSD 2009


2009/10/9 zepolen <zepolen at gmail.com>:
> It doesn't seem to be tracking the request time properly, the counts
> remain at 0 for all ranges.

Oops. I never intended to include the request time counter in the patch.
Attached is an updated patch which adds the required _add_int() call.

-- 
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_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/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/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_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_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-09 09:39:03.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,24 @@
     }
 
 #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);
+
+    {
+#if (NGX_HTTP_REQUEST_TIME_ALWAYS_UPDATE)
+        ngx_time_update(0, 0);
+#endif
+        ngx_time_t *tp = ngx_timeofday();
+        (void) ngx_statistics_add_int(ngx_stat_http_request_time_key, (tp->sec - r->start_sec) * 1000 + tp->msec - r->start_msec);
+    }
+#endif
 
     if (error && r->headers_out.status == 0) {
         r->headers_out.status = error;
@@ -2874,6 +2919,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_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