├── config ├── .travis.yml ├── t └── sanity.t ├── LICENSE ├── ngx_http_status.h ├── README.md └── ngx_http_status_module.c /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_status_module 2 | 3 | HTTP_STATUS_SRCS="$ngx_addon_dir/ngx_http_status_module.c" 4 | HTTP_STATUS_DEPS="$ngx_addon_dir/ngx_http_status.h" 5 | 6 | if test -n "$ngx_module_link"; then 7 | ngx_module_type=HTTP 8 | ngx_module_name=$ngx_addon_name 9 | ngx_module_incs= 10 | ngx_module_deps="$HTTP_STATUS_DEPS" 11 | ngx_module_srcs="$HTTP_STATUS_SRCS" 12 | ngx_module_libs= 13 | 14 | . auto/module 15 | else 16 | HTTP_MODULES="$HTTP_MODULES ngx_http_status_module" 17 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_STATUS_SRCS" 18 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HTTP_STATUS_DEPS" 19 | fi 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 5.20 3 | sudo: false 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - gcc-4.8 11 | 12 | env: 13 | matrix: 14 | - VER_NGINX=1.6.3 15 | - VER_NGINX=1.8.0 16 | - VER_NGINX=1.9.4 17 | 18 | install: 19 | - mkdir ./vendor && cd ./vendor 20 | 21 | - wget "http://nginx.org/download/nginx-${VER_NGINX}.tar.gz" -O nginx.tar.gz && tar -xf nginx.tar.gz 22 | 23 | - cd "${TRAVIS_BUILD_DIR}/vendor/nginx-${VER_NGINX}" 24 | - ./configure --add-module="${TRAVIS_BUILD_DIR}" 25 | - make 26 | - export PATH="$PATH:${TRAVIS_BUILD_DIR}/vendor/nginx-${VER_NGINX}/objs" 27 | 28 | - cpanm -v --notest Test::Nginx 29 | 30 | script: cd ${TRAVIS_BUILD_DIR} && prove 31 | -------------------------------------------------------------------------------- /t/sanity.t: -------------------------------------------------------------------------------- 1 | #vi:filetype=perl 2 | 3 | use lib 'lib'; 4 | use Test::Nginx::Socket; 5 | 6 | repeat_each(3); 7 | 8 | plan tests => repeat_each() * (blocks() * 1 + 1); 9 | no_root_location(); 10 | no_long_string(); 11 | $ENV{TEST_NGINX_SERVROOT} = server_root(); 12 | run_tests(); 13 | 14 | __DATA__ 15 | === TEST 1: Basic GET request 16 | 17 | --- http_config 18 | http_status_zone host "$host,$server_addr:$server_port" 10M; 19 | 20 | --- config 21 | http_status host; 22 | location / { 23 | root html; 24 | index index.html index.htm; 25 | } 26 | location /t { 27 | http_status_show; 28 | } 29 | --- request 30 | GET /t 31 | --- error_code: 200 32 | --- response_body_like 33 | \s?|localhost,127.0.0.1:1984,55,129,1,1,1,0,0,0,0,0,0,0,0|localhost,127.0.0.1:1984,110,318,2,2,2,0,0,0,0,0,0,0,0 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sophos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ngx_http_status.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | typedef struct ngx_http_status_rbnode_s ngx_http_status_rbnode_t; 8 | 9 | 10 | struct ngx_http_status_rbnode_s { 11 | u_char color; 12 | u_char padding[3]; 13 | uint32_t len; 14 | ngx_queue_t queue; 15 | ngx_atomic_t bytes_in; 16 | ngx_atomic_t bytes_out; 17 | ngx_atomic_t conn_total; 18 | ngx_atomic_t req_total; 19 | ngx_atomic_t http_2xx; 20 | ngx_atomic_t http_3xx; 21 | ngx_atomic_t http_4xx; 22 | ngx_atomic_t http_5xx; 23 | ngx_atomic_t other_status; 24 | ngx_atomic_t rt; 25 | ngx_atomic_t ureq; 26 | ngx_atomic_t urt; 27 | ngx_atomic_t utries; 28 | u_char data[1]; 29 | }; 30 | 31 | 32 | typedef struct { 33 | ngx_array_t *monitor; 34 | ngx_array_t *display; 35 | ngx_array_t *bypass; 36 | } ngx_http_status_conf_t; 37 | 38 | 39 | typedef struct { 40 | ngx_rbtree_t rbtree; 41 | ngx_rbtree_node_t sentinel; 42 | ngx_queue_t queue; 43 | } ngx_http_status_shctx_t; 44 | 45 | 46 | typedef struct { 47 | ngx_str_t *val; 48 | ngx_slab_pool_t *shpool; 49 | ngx_http_status_shctx_t *sh; 50 | ngx_http_complex_value_t value; 51 | } ngx_http_status_ctx_t; 52 | 53 | 54 | #define NGX_HTTP_STATUS_BYTES_IN \ 55 | offsetof(ngx_http_status_rbnode_t, bytes_in) 56 | 57 | #define NGX_HTTP_STATUS_BYTES_OUT \ 58 | offsetof(ngx_http_status_rbnode_t, bytes_out) 59 | 60 | #define NGX_HTTP_STATUS_CONN_TOTAL \ 61 | offsetof(ngx_http_status_rbnode_t, conn_total) 62 | 63 | #define NGX_HTTP_STATUS_REQ_TOTAL \ 64 | offsetof(ngx_http_status_rbnode_t, req_total) 65 | 66 | #define NGX_HTTP_STATUS_2XX \ 67 | offsetof(ngx_http_status_rbnode_t, http_2xx) 68 | 69 | #define NGX_HTTP_STATUS_3XX \ 70 | offsetof(ngx_http_status_rbnode_t, http_3xx) 71 | 72 | #define NGX_HTTP_STATUS_4XX \ 73 | offsetof(ngx_http_status_rbnode_t, http_4xx) 74 | 75 | #define NGX_HTTP_STATUS_5XX \ 76 | offsetof(ngx_http_status_rbnode_t, http_5xx) 77 | 78 | #define NGX_HTTP_STATUS_OTHER_STATUS \ 79 | offsetof(ngx_http_status_rbnode_t, other_status) 80 | 81 | #define NGX_HTTP_STATUS_RT \ 82 | offsetof(ngx_http_status_rbnode_t, rt) 83 | 84 | #define NGX_HTTP_STATUS_UPS_REQ \ 85 | offsetof(ngx_http_status_rbnode_t, ureq) 86 | 87 | #define NGX_HTTP_STATUS_UPS_RT \ 88 | offsetof(ngx_http_status_rbnode_t, urt) 89 | 90 | #define NGX_HTTP_STATUS_UPS_TRIES \ 91 | offsetof(ngx_http_status_rbnode_t, utries) 92 | 93 | #define REQ_FIELD(node, offset) \ 94 | ((ngx_atomic_t *) ((char *) node + offset)) 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | status-nginx-module 2 | =================== 3 | ![](https://travis-ci.org/hnlq715/status-nginx-module.svg) 4 | 5 | The req status module for pure nginx. The core source file comes from Tengine, which is developed and maintained by Alibaba. 6 | 7 | 8 | Description 9 | =========== 10 | 11 | This module will help monitor running status of Nginx. 12 | * It can provide running status information of Nginx. 13 | * The information is divided into different zones, and each zone is independent. 14 | * The status information is about connections, requests, response status codes, input and output flows, rt, and upstreams. 15 | * It shows all the results by default, and can be set to show part of them by specifying zones. 16 | 17 | 18 | Compilation 19 | =========== 20 | 21 | ``` 22 | git clone https://github.com/hnlq715/status-nginx-module 23 | ./configure --add-module=/path/to/status-nginx-module 24 | make && make install 25 | ``` 26 | 27 | Starting from NGINX 1.9.11, you can also compile this module as a dynamic 28 | module, by using the `--add-dynamic-module=PATH` option instead of 29 | `--add-module=PATH` on the ./configure command line above. And then you can 30 | explicitly load the module in your `nginx.conf` via the *load\_module* directive. 31 | 32 | 33 | Configuration Directives 34 | ======================== 35 | 36 | http\_status\_zone 37 | ------------------ 38 | **syntax:** *http\_status\_zone <zone\_name> <row\_name> <shm\_size>* \ 39 | **default:** *no* \ 40 | **context:** *http* 41 | 42 | Sets up shared memory zone named *zone_name* for statistics. Statistics are 43 | simple table where first column is key and its value is computed by evaluating 44 | expression *row\_name*. Remaining columns are collected metrics (see 45 | [below](#format-of-statistics-output)). 46 | *shm_size* specifies reserved memory for the statistics, each row will consume 47 | about 200 bytes of memory (this is rough estimation, see source code for exact 48 | value). If request should create new statistics row and there is no more memory 49 | in zone, then it is not counted in this zone and appropriate warning is logged. 50 | 51 | http\_status 52 | ------------ 53 | **syntax:** *http\_status <zone\_name> [<zone\_name> ...];* \ 54 | **default:** *no* \ 55 | **context:** *http*, *server*, *location* 56 | 57 | Takes list of zones (at least one) where request should be counted. 58 | 59 | http\_status\_bypass 60 | --------------------- 61 | **syntax:** *http\_status\_bypass <condition> [<condition> ...];* \ 62 | **default:** *no* \ 63 | **context:** *http*, *server*, *location* 64 | 65 | Defines conditions under which request will not be counted in statistics at 66 | all. If at least one value of the string parameters is not empty and is not 67 | equal to “0” then the request will not be counted. 68 | 69 | http\_status\_show 70 | ------------ 71 | **syntax:** *http\_status\_show [<zone\_name> [<zone\_name> ...]];* \ 72 | **default:** *no* \ 73 | **context:** *location* 74 | 75 | Renders statistics as a response to the request matching current location. 76 | No Content-Type response header is set. 77 | 78 | 79 | Variables 80 | ========= 81 | 82 | $upstream\_first\_addr 83 | ---------------------- 84 | 85 | This module defines variable *$upstream\_first\_addr* which contains 86 | address of first upstream that was contacted when handling current request. 87 | 88 | 89 | Format of statistics output 90 | =========================== 91 | Statistics are formatted as comma separated values (without quoting, thus you 92 | can create more virtual columns by having commas in *row\_name*). No header row 93 | is rendered. 94 | 95 | * **row\_name** - value of expression defined by the directive `http_status_zone` 96 | * **bytesintotal** - total number of bytes received from client 97 | * **bytesouttotal** - total number of bytes sent to client 98 | * **conn\_total** - total number of accepted connections 99 | * **req\_total** - total number of processed requests 100 | * **2xx** - total number of 2xx requests 101 | * **3xx** - total number of 3xx requests 102 | * **4xx** - total number of 4xx requests 103 | * **5xx** - total number of 5xx requests 104 | * **other total** - number of other requests 105 | * **rt\_total** - accumulated request time (miliseconds) 106 | * **upstream\_req** - total number of requests calling for upstream 107 | * **upstream\_rt** - accumulated time of upstream calls (miliseconds) 108 | * **upstream\_tries** - total number of calls for upstream 109 | 110 | 111 | Example 112 | ======= 113 | 114 | ``` 115 | http { 116 | # Set up statistics zone named 'host'. 117 | # ( Name of shared memory zone cannot be used in other 118 | # places using named shared memory zones (proxy_cache_path ... keys_zone=..., ). ) 119 | http_status_zone host "$host,$server_addr:$server_port" 10M; 120 | 121 | # Prepare variable for statistics with custom differrentiation. 122 | # ( We cannot use 'set' in 'http' block, so we use map and set it to "$server_name" by default, 123 | # with mapping of server_name '_' to "unnamed_server" ); 124 | map "$server_name" $custom_stat_name { 125 | default "$server_name"; 126 | _ unnamed_server; 127 | } 128 | http_status_zone custom_stats "_CUSTOM,$custom_stat_name" 10M; 129 | 130 | # Count served pages only in zone 'host' by default 131 | http_status host; 132 | 133 | 134 | server { 135 | server_name _; 136 | 137 | location /all-stats { 138 | #Display rows from all zones together 139 | http_status_show; 140 | } 141 | 142 | location /host-stats { 143 | #Display rows only from 'host' zone; 144 | http_status_show host; 145 | } 146 | 147 | location /all-stats-ex { 148 | #Display rows from both zones (defined explicitly for configuration demonstration) 149 | http_status_show host custom_stats; 150 | 151 | #Override custom_stats_name when counting accesses to this location 152 | set $custom_stat_name "all stats explicit"; 153 | } 154 | 155 | # Count all accesses to this server in both statistics zones. 156 | http_status host custom_stats; 157 | } 158 | } 159 | ``` 160 | 161 | When you call '/all-stats', you will get results like this: 162 | ``` 163 | localhost,127.0.0.1:80,162,6242,1,1,1,0,0,0,0,10,1,10,1 164 | _CUSTOM,unnamed_server,6242,1,1,1,0,0,0,0,10,1,10,1 165 | ``` 166 | -------------------------------------------------------------------------------- /ngx_http_status_module.c: -------------------------------------------------------------------------------- 1 | #include "ngx_http_status.h" 2 | 3 | typedef struct { 4 | ngx_array_t monitor_index; 5 | ngx_flag_t bypass; 6 | } ngx_http_status_store_t; 7 | 8 | off_t ngx_http_status_fields[13] = { 9 | NGX_HTTP_STATUS_BYTES_IN, 10 | NGX_HTTP_STATUS_BYTES_OUT, 11 | NGX_HTTP_STATUS_CONN_TOTAL, 12 | NGX_HTTP_STATUS_REQ_TOTAL, 13 | NGX_HTTP_STATUS_2XX, 14 | NGX_HTTP_STATUS_3XX, 15 | NGX_HTTP_STATUS_4XX, 16 | NGX_HTTP_STATUS_5XX, 17 | NGX_HTTP_STATUS_OTHER_STATUS, 18 | NGX_HTTP_STATUS_RT, 19 | NGX_HTTP_STATUS_UPS_REQ, 20 | NGX_HTTP_STATUS_UPS_RT, 21 | NGX_HTTP_STATUS_UPS_TRIES 22 | }; 23 | 24 | 25 | static void *ngx_http_status_create_main_conf(ngx_conf_t *cf); 26 | static void *ngx_http_status_create_loc_conf(ngx_conf_t *cf); 27 | static char *ngx_http_status_merge_loc_conf(ngx_conf_t *cf, void *parent, 28 | void *child); 29 | static ngx_int_t ngx_http_status_init(ngx_conf_t *cf); 30 | static char *ngx_http_status_show(ngx_conf_t *cf, ngx_command_t *cmd, 31 | void *conf); 32 | static char *ngx_http_status_zone(ngx_conf_t *cf, ngx_command_t *cmd, 33 | void *conf); 34 | static char *ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, 35 | void *conf); 36 | static void ngx_http_status_count(void *data, off_t offset, 37 | ngx_int_t incr); 38 | static ngx_int_t ngx_http_status_init_zone(ngx_shm_zone_t *shm_zone, 39 | void *data); 40 | 41 | static ngx_int_t ngx_http_status_log_handler(ngx_http_request_t *r); 42 | static ngx_int_t ngx_http_status_show_handler(ngx_http_request_t *r); 43 | 44 | static ngx_http_status_rbnode_t * 45 | ngx_http_status_rbtree_lookup(ngx_shm_zone_t *shm_zone, 46 | ngx_str_t *val); 47 | static void ngx_http_status_rbtree_insert_value(ngx_rbtree_node_t *temp, 48 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); 49 | static ngx_http_status_store_t * 50 | ngx_http_status_create_store(ngx_http_request_t *r, 51 | ngx_http_status_conf_t *slcf); 52 | static ngx_int_t 53 | ngx_http_status_add_variables(ngx_conf_t *cf); 54 | static ngx_int_t 55 | ngx_http_status_upstream_first_addr_variable(ngx_http_request_t *r, 56 | ngx_http_variable_value_t *v, uintptr_t data); 57 | 58 | 59 | static ngx_command_t ngx_http_status_commands[] = { 60 | 61 | { ngx_string("http_status_zone"), 62 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, 63 | ngx_http_status_zone, 64 | 0, 65 | 0, 66 | NULL }, 67 | 68 | { ngx_string("http_status"), 69 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 70 | ngx_http_reqstat, 71 | NGX_HTTP_LOC_CONF_OFFSET, 72 | 0, 73 | NULL }, 74 | 75 | { ngx_string("http_status_bypass"), 76 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 77 | ngx_http_set_predicate_slot, 78 | NGX_HTTP_LOC_CONF_OFFSET, 79 | offsetof(ngx_http_status_conf_t, bypass), 80 | NULL }, 81 | 82 | { ngx_string("http_status_show"), 83 | NGX_HTTP_LOC_CONF|NGX_CONF_ANY, 84 | ngx_http_status_show, 85 | NGX_HTTP_LOC_CONF_OFFSET, 86 | 0, 87 | NULL }, 88 | 89 | ngx_null_command 90 | }; 91 | 92 | 93 | static ngx_http_module_t ngx_http_status_module_ctx = { 94 | ngx_http_status_add_variables, /* preconfiguration */ 95 | ngx_http_status_init, /* postconfiguration */ 96 | 97 | ngx_http_status_create_main_conf, /* create main configuration */ 98 | NULL, /* init main configuration */ 99 | 100 | NULL, /* create server configuration */ 101 | NULL, /* merge server configuration */ 102 | 103 | ngx_http_status_create_loc_conf, /* create location configuration */ 104 | ngx_http_status_merge_loc_conf /* merge location configuration */ 105 | }; 106 | 107 | 108 | ngx_module_t ngx_http_status_module = { 109 | NGX_MODULE_V1, 110 | &ngx_http_status_module_ctx, /* module context */ 111 | ngx_http_status_commands, /* module directives */ 112 | NGX_HTTP_MODULE, /* module type */ 113 | NULL, /* init master */ 114 | NULL, /* init module */ 115 | NULL, /* init process */ 116 | NULL, /* init thread */ 117 | NULL, /* exit thread */ 118 | NULL, /* exit process */ 119 | NULL, /* exit master */ 120 | NGX_MODULE_V1_PADDING 121 | }; 122 | 123 | static ngx_http_variable_t ngx_http_status_vars[] = { 124 | 125 | { ngx_string("upstream_first_addr"), NULL, 126 | ngx_http_status_upstream_first_addr_variable, 0, 127 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 128 | 129 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 130 | }; 131 | 132 | 133 | static void * 134 | ngx_http_status_create_main_conf(ngx_conf_t *cf) 135 | { 136 | return ngx_pcalloc(cf->pool, sizeof(ngx_http_status_conf_t)); 137 | } 138 | 139 | 140 | static void * 141 | ngx_http_status_create_loc_conf(ngx_conf_t *cf) 142 | { 143 | ngx_http_status_conf_t *conf; 144 | 145 | conf = ngx_palloc(cf->pool, sizeof(ngx_http_status_conf_t)); 146 | if (conf == NULL) { 147 | return NULL; 148 | } 149 | 150 | conf->bypass = NGX_CONF_UNSET_PTR; 151 | conf->monitor = NGX_CONF_UNSET_PTR; 152 | conf->display = NGX_CONF_UNSET_PTR; 153 | 154 | return conf; 155 | } 156 | 157 | 158 | static char * 159 | ngx_http_status_merge_loc_conf(ngx_conf_t *cf, void *parent, 160 | void *child) 161 | { 162 | ngx_http_status_conf_t *conf = child; 163 | ngx_http_status_conf_t *prev = parent; 164 | 165 | ngx_conf_merge_ptr_value(conf->bypass, prev->bypass, NULL); 166 | ngx_conf_merge_ptr_value(conf->monitor, prev->monitor, NULL); 167 | ngx_conf_merge_ptr_value(conf->display, prev->display, NULL); 168 | 169 | return NGX_CONF_OK; 170 | } 171 | 172 | 173 | static ngx_int_t 174 | ngx_http_status_init(ngx_conf_t *cf) 175 | { 176 | ngx_http_handler_pt *h; 177 | ngx_http_core_main_conf_t *cmcf; 178 | 179 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 180 | 181 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); 182 | if (h == NULL) { 183 | return NGX_ERROR; 184 | } 185 | 186 | *h = ngx_http_status_log_handler; 187 | 188 | 189 | return NGX_OK; 190 | } 191 | 192 | 193 | static char * 194 | ngx_http_status_show(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 195 | { 196 | ngx_str_t *value; 197 | ngx_uint_t i; 198 | ngx_shm_zone_t *shm_zone, **z; 199 | ngx_http_core_loc_conf_t *clcf; 200 | 201 | ngx_http_status_conf_t *slcf = conf; 202 | 203 | value = cf->args->elts; 204 | 205 | if (slcf->display != NGX_CONF_UNSET_PTR) { 206 | return "is duplicate"; 207 | } 208 | 209 | if (cf->args->nelts == 1) { 210 | slcf->display = NULL; 211 | goto reg_handler; 212 | } 213 | 214 | slcf->display = ngx_array_create(cf->pool, cf->args->nelts - 1, 215 | sizeof(ngx_shm_zone_t *)); 216 | if (slcf->display == NULL) { 217 | return NGX_CONF_ERROR; 218 | } 219 | 220 | for (i = 1; i < cf->args->nelts; i++) { 221 | shm_zone = ngx_shared_memory_add(cf, &value[i], 0, 222 | &ngx_http_status_module); 223 | if (shm_zone == NULL) { 224 | return NGX_CONF_ERROR; 225 | } 226 | 227 | z = ngx_array_push(slcf->display); 228 | *z = shm_zone; 229 | } 230 | 231 | reg_handler: 232 | 233 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 234 | clcf->handler = ngx_http_status_show_handler; 235 | 236 | return NGX_CONF_OK; 237 | } 238 | 239 | 240 | static char * 241 | ngx_http_status_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 242 | { 243 | ssize_t size; 244 | ngx_str_t *value; 245 | ngx_shm_zone_t *shm_zone; 246 | ngx_http_status_ctx_t *ctx; 247 | ngx_http_compile_complex_value_t ccv; 248 | 249 | value = cf->args->elts; 250 | 251 | size = ngx_parse_size(&value[3]); 252 | if (size == NGX_ERROR) { 253 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 254 | "invalid zone size \"%V\"", &value[3]); 255 | return NGX_CONF_ERROR; 256 | } 257 | 258 | if (size < (ssize_t) (8 * ngx_pagesize)) { 259 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 260 | "zone \"%V\" is too small", &value[1]); 261 | return NGX_CONF_ERROR; 262 | } 263 | 264 | ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_status_ctx_t)); 265 | if (ctx == NULL) { 266 | return NGX_CONF_ERROR; 267 | } 268 | 269 | if (ngx_http_script_variables_count(&value[2]) == 0) { 270 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 271 | "the value \"%V\" is a constant", 272 | &value[2]); 273 | return NGX_CONF_ERROR; 274 | } 275 | 276 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 277 | 278 | ccv.cf = cf; 279 | ccv.value = &value[2]; 280 | ccv.complex_value = &ctx->value; 281 | 282 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 283 | return NGX_CONF_ERROR; 284 | } 285 | 286 | ctx->val = ngx_palloc(cf->pool, sizeof(ngx_str_t)); 287 | if (ctx->val == NULL) { 288 | return NGX_CONF_ERROR; 289 | } 290 | *ctx->val = value[2]; 291 | 292 | shm_zone = ngx_shared_memory_add(cf, &value[1], size, 293 | &ngx_http_status_module); 294 | if (shm_zone == NULL) { 295 | return NGX_CONF_ERROR; 296 | } 297 | 298 | if (shm_zone->data) { 299 | ctx = shm_zone->data; 300 | 301 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 302 | "%V \"%V\" is already bound to value \"%V\"", 303 | &cmd->name, &value[1], ctx->val); 304 | return NGX_CONF_ERROR; 305 | } 306 | 307 | shm_zone->init = ngx_http_status_init_zone; 308 | shm_zone->data = ctx; 309 | 310 | return NGX_CONF_OK; 311 | } 312 | 313 | 314 | static char * 315 | ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 316 | { 317 | ngx_str_t *value; 318 | ngx_uint_t i, j; 319 | ngx_shm_zone_t *shm_zone, **z; 320 | ngx_http_status_conf_t *smcf; 321 | 322 | ngx_http_status_conf_t *slcf = conf; 323 | 324 | value = cf->args->elts; 325 | smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_status_module); 326 | 327 | if (slcf->monitor != NGX_CONF_UNSET_PTR) { 328 | return "is duplicate"; 329 | } 330 | 331 | if (smcf->monitor == NULL) { 332 | smcf->monitor = ngx_array_create(cf->pool, cf->args->nelts - 1, 333 | sizeof(ngx_shm_zone_t *)); 334 | if (smcf->monitor == NULL) { 335 | return NGX_CONF_ERROR; 336 | } 337 | } 338 | 339 | slcf->monitor = ngx_array_create(cf->pool, cf->args->nelts - 1, 340 | sizeof(ngx_shm_zone_t *)); 341 | if (slcf->monitor == NULL) { 342 | return NGX_CONF_ERROR; 343 | } 344 | 345 | for (i = 1; i < cf->args->nelts; i++) { 346 | shm_zone = ngx_shared_memory_add(cf, &value[i], 0, 347 | &ngx_http_status_module); 348 | if (shm_zone == NULL) { 349 | return NGX_CONF_ERROR; 350 | } 351 | 352 | z = ngx_array_push(slcf->monitor); 353 | *z = shm_zone; 354 | 355 | z = smcf->monitor->elts; 356 | for (j = 0; j < smcf->monitor->nelts; j++) { 357 | if (!ngx_strcmp(value[i].data, z[j]->shm.name.data)) { 358 | break; 359 | } 360 | } 361 | 362 | if (j == smcf->monitor->nelts) { 363 | z = ngx_array_push(smcf->monitor); 364 | if (z == NULL) { 365 | return NGX_CONF_ERROR; 366 | } 367 | *z = shm_zone; 368 | } 369 | } 370 | 371 | return NGX_CONF_OK; 372 | } 373 | 374 | 375 | static ngx_int_t 376 | ngx_http_status_log_handler(ngx_http_request_t *r) 377 | { 378 | ngx_uint_t i, j, status, utries; 379 | ngx_time_t *tp; 380 | ngx_msec_int_t ms, total_ms; 381 | ngx_http_status_conf_t *slcf; 382 | ngx_http_status_rbnode_t *fnode, **fnode_store; 383 | ngx_http_upstream_state_t *state; 384 | ngx_http_status_store_t *store; 385 | 386 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_status_module); 387 | 388 | if (slcf->monitor == NULL) { 389 | return NGX_OK; 390 | } 391 | 392 | store = ngx_http_get_module_ctx(r, ngx_http_status_module); 393 | 394 | if (store == NULL) { 395 | store = ngx_http_status_create_store(r, slcf); 396 | if (store == NULL) { 397 | return NGX_ERROR; 398 | } 399 | } 400 | 401 | if (store->bypass) { 402 | return NGX_OK; 403 | } 404 | 405 | fnode_store = store->monitor_index.elts; 406 | for (i = 0; i < store->monitor_index.nelts; i++) { 407 | fnode = fnode_store[i]; 408 | if (r->connection->requests == 1) { 409 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_CONN_TOTAL, 1); 410 | } 411 | 412 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_REQ_TOTAL, 1); 413 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_BYTES_IN, 414 | r->request_length); 415 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_BYTES_OUT, 416 | r->connection->sent); 417 | 418 | if (r->err_status) { 419 | status = r->err_status; 420 | 421 | } else if (r->headers_out.status) { 422 | status = r->headers_out.status; 423 | 424 | } else if (r->http_version == NGX_HTTP_VERSION_9) { 425 | status = 9; 426 | 427 | } else { 428 | status = 0; 429 | } 430 | 431 | if (status >= 200 && status < 300) { 432 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_2XX, 1); 433 | 434 | } else if (status >= 300 && status < 400) { 435 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_3XX, 1); 436 | 437 | } else if (status >= 400 && status < 500) { 438 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_4XX, 1); 439 | 440 | } else if (status >= 500 && status < 600) { 441 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_5XX, 1); 442 | 443 | } else { 444 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_OTHER_STATUS, 1); 445 | } 446 | 447 | tp = ngx_timeofday(); 448 | 449 | ms = (ngx_msec_int_t) 450 | ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)); 451 | ms = ngx_max(ms, 0); 452 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_RT, ms); 453 | 454 | if (r->upstream_states != NULL && r->upstream_states->nelts > 0) { 455 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_UPS_REQ, 1); 456 | 457 | j = 0; 458 | total_ms = 0; 459 | utries = 0; 460 | state = r->upstream_states->elts; 461 | 462 | for ( ;; ) { 463 | 464 | utries++; 465 | 466 | #if (nginx_version < 1009001) 467 | ms = (ngx_msec_int_t) (state[j].response_sec * 1000 468 | + state[j].response_msec); 469 | #else 470 | ms = (ngx_msec_int_t) (state[j].connect_time 471 | + state[j].header_time + state[j].response_time); 472 | #endif 473 | 474 | ms = ngx_max(ms, 0); 475 | total_ms += ms; 476 | 477 | if (++j == r->upstream_states->nelts) { 478 | break; 479 | } 480 | 481 | if (state[j].peer == NULL) { 482 | if (++j == r->upstream_states->nelts) { 483 | break; 484 | } 485 | } 486 | } 487 | 488 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_UPS_RT, 489 | total_ms); 490 | ngx_http_status_count(fnode, NGX_HTTP_STATUS_UPS_TRIES, 491 | utries); 492 | } 493 | } 494 | 495 | return NGX_OK; 496 | } 497 | 498 | 499 | static ngx_int_t 500 | ngx_http_status_show_handler(ngx_http_request_t *r) 501 | { 502 | ngx_int_t rc; 503 | ngx_buf_t *b; 504 | ngx_uint_t i, j; 505 | ngx_array_t *display; 506 | ngx_chain_t *tl, *free, *busy; 507 | ngx_queue_t *q; 508 | ngx_shm_zone_t **shm_zone; 509 | ngx_http_status_ctx_t *ctx; 510 | ngx_http_status_conf_t *slcf; 511 | ngx_http_status_conf_t *smcf; 512 | ngx_http_status_rbnode_t *node; 513 | 514 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_status_module); 515 | smcf = ngx_http_get_module_main_conf(r, ngx_http_status_module); 516 | 517 | display = slcf->display == NULL ? smcf->monitor : slcf->display; 518 | if (display == NULL) { 519 | r->headers_out.status = NGX_HTTP_NO_CONTENT; 520 | return ngx_http_send_header(r); 521 | } 522 | 523 | r->headers_out.status = NGX_HTTP_OK; 524 | ngx_http_clear_content_length(r); 525 | 526 | rc = ngx_http_send_header(r); 527 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { 528 | return rc; 529 | } 530 | 531 | shm_zone = display->elts; 532 | 533 | for (free = busy = NULL, i = 0; i < display->nelts; i++) { 534 | 535 | ctx = shm_zone[i]->data; 536 | 537 | for (q = ngx_queue_head(&ctx->sh->queue); 538 | q != ngx_queue_sentinel(&ctx->sh->queue); 539 | q = ngx_queue_next(q)) 540 | { 541 | node = ngx_queue_data(q, ngx_http_status_rbnode_t, queue); 542 | 543 | tl = ngx_chain_get_free_buf(r->pool, &free); 544 | if (tl == NULL) { 545 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 546 | } 547 | 548 | b = tl->buf; 549 | if (b->start == NULL) { 550 | b->start = ngx_pcalloc(r->pool, 512); 551 | if (b->start == NULL) { 552 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 553 | } 554 | 555 | b->end = b->start + 512; 556 | } 557 | 558 | b->last = b->pos = b->start; 559 | b->memory = 1; 560 | b->temporary = 1; 561 | 562 | b->last = ngx_slprintf(b->last, b->end, "%*s,", 563 | (size_t) node->len, node->data); 564 | 565 | for (j = 0; 566 | j < sizeof(ngx_http_status_fields) / sizeof(off_t); 567 | j++) 568 | { 569 | b->last = ngx_slprintf(b->last, b->end, "%uA,", 570 | *REQ_FIELD(node, 571 | ngx_http_status_fields[j])); 572 | } 573 | 574 | *(b->last - 1) = '\n'; 575 | 576 | if (ngx_http_output_filter(r, tl) == NGX_ERROR) { 577 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 578 | } 579 | 580 | #if nginx_version >= 1002000 581 | ngx_chain_update_chains(r->pool, &free, &busy, &tl, 582 | (ngx_buf_tag_t) &ngx_http_status_module); 583 | #else 584 | ngx_chain_update_chains(&free, &busy, &tl, 585 | (ngx_buf_tag_t) &ngx_http_status_module); 586 | #endif 587 | } 588 | } 589 | 590 | tl = ngx_chain_get_free_buf(r->pool, &free); 591 | if (tl == NULL) { 592 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 593 | } 594 | 595 | b = tl->buf; 596 | b->last_buf = 1; 597 | 598 | return ngx_http_output_filter(r, tl); 599 | } 600 | 601 | 602 | void 603 | ngx_http_status_count(void *data, off_t offset, ngx_int_t incr) 604 | { 605 | ngx_http_status_rbnode_t *node = data; 606 | 607 | (void) ngx_atomic_fetch_add(REQ_FIELD(node, offset), incr); 608 | } 609 | 610 | 611 | static ngx_http_status_rbnode_t * 612 | ngx_http_status_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) 613 | { 614 | size_t size; 615 | uint32_t hash; 616 | ngx_int_t rc; 617 | ngx_rbtree_node_t *node, *sentinel; 618 | ngx_http_status_ctx_t *ctx; 619 | ngx_http_status_rbnode_t *rs; 620 | 621 | ctx = shm_zone->data; 622 | 623 | hash = ngx_crc32_short(val->data, val->len); 624 | 625 | node = ctx->sh->rbtree.root; 626 | sentinel = ctx->sh->rbtree.sentinel; 627 | ngx_shmtx_lock(&ctx->shpool->mutex); 628 | 629 | while (node != sentinel) { 630 | 631 | if (hash < node->key) { 632 | node = node->left; 633 | continue; 634 | } 635 | 636 | if (hash > node->key) { 637 | node = node->right; 638 | continue; 639 | } 640 | 641 | /* hash == node->key */ 642 | 643 | rs = (ngx_http_status_rbnode_t *) &node->color; 644 | 645 | rc = ngx_memn2cmp(val->data, rs->data, val->len, (size_t) rs->len); 646 | 647 | if (rc == 0) { 648 | ngx_shmtx_unlock(&ctx->shpool->mutex); 649 | return rs; 650 | } 651 | 652 | node = (rc < 0) ? node->left : node->right; 653 | } 654 | 655 | size = offsetof(ngx_rbtree_node_t, color) 656 | + offsetof(ngx_http_status_rbnode_t, data) 657 | + val->len; 658 | 659 | node = ngx_slab_alloc_locked(ctx->shpool, size); 660 | if (node == NULL) { 661 | ngx_shmtx_unlock(&ctx->shpool->mutex); 662 | return NULL; 663 | } 664 | 665 | node->key = hash; 666 | 667 | rs = (ngx_http_status_rbnode_t *) &node->color; 668 | 669 | rs->len = val->len; 670 | 671 | ngx_memcpy(rs->data, val->data, val->len); 672 | 673 | ngx_rbtree_insert(&ctx->sh->rbtree, node); 674 | 675 | ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); 676 | 677 | ngx_shmtx_unlock(&ctx->shpool->mutex); 678 | 679 | return rs; 680 | } 681 | 682 | 683 | static ngx_int_t 684 | ngx_http_status_init_zone(ngx_shm_zone_t *shm_zone, void *data) 685 | { 686 | ngx_http_status_ctx_t *ctx, *octx; 687 | 688 | octx = data; 689 | ctx = shm_zone->data; 690 | 691 | if (octx != NULL) { 692 | if (ngx_strcmp(ctx->val->data, octx->val->data) != 0) { 693 | ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, 694 | "reqstat \"%V\" uses the value str \"%V\" " 695 | "while previously it used \"%V\"", 696 | &shm_zone->shm.name, ctx->val, octx->val); 697 | return NGX_ERROR; 698 | } 699 | 700 | ctx->shpool = octx->shpool; 701 | ctx->sh = octx->sh; 702 | 703 | return NGX_OK; 704 | } 705 | 706 | ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 707 | 708 | ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_status_shctx_t)); 709 | if (ctx->sh == NULL) { 710 | return NGX_ERROR; 711 | } 712 | 713 | ctx->shpool->data = ctx->sh; 714 | 715 | ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, 716 | ngx_http_status_rbtree_insert_value); 717 | 718 | ngx_queue_init(&ctx->sh->queue); 719 | 720 | return NGX_OK; 721 | } 722 | 723 | 724 | static void 725 | ngx_http_status_rbtree_insert_value(ngx_rbtree_node_t *temp, 726 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) 727 | { 728 | ngx_rbtree_node_t **p; 729 | ngx_http_status_rbnode_t *rsn, *rsnt; 730 | 731 | for ( ;; ) { 732 | 733 | if (node->key < temp->key) { 734 | 735 | p = &temp->left; 736 | 737 | } else if (node->key > temp->key) { 738 | 739 | p = &temp->right; 740 | 741 | } else { /* node->key == temp->key */ 742 | 743 | rsn = (ngx_http_status_rbnode_t *) &node->color; 744 | rsnt = (ngx_http_status_rbnode_t *) &temp->color; 745 | 746 | p = (ngx_memn2cmp(rsn->data, rsnt->data, rsn->len, rsnt->len) < 0) 747 | ? &temp->left : &temp->right; 748 | } 749 | 750 | if (*p == sentinel) { 751 | break; 752 | } 753 | 754 | temp = *p; 755 | } 756 | 757 | *p = node; 758 | node->parent = temp; 759 | node->left = sentinel; 760 | node->right = sentinel; 761 | ngx_rbt_red(node); 762 | } 763 | 764 | 765 | static ngx_http_status_store_t * 766 | ngx_http_status_create_store(ngx_http_request_t *r, 767 | ngx_http_status_conf_t *slcf) 768 | { 769 | ngx_str_t val; 770 | ngx_uint_t i; 771 | ngx_shm_zone_t **shm_zone, *z; 772 | ngx_http_status_ctx_t *ctx; 773 | ngx_http_status_store_t *store; 774 | ngx_http_status_rbnode_t *fnode, **fnode_store; 775 | 776 | store = ngx_pcalloc(r->pool, sizeof(ngx_http_status_store_t)); 777 | if (store == NULL) { 778 | return NULL; 779 | } 780 | 781 | switch (ngx_http_test_predicates(r, slcf->bypass)) { 782 | 783 | case NGX_ERROR: 784 | return NULL; 785 | 786 | case NGX_DECLINED: 787 | store->bypass = 1; 788 | return store; 789 | 790 | default: /* NGX_OK */ 791 | break; 792 | } 793 | 794 | if (ngx_array_init(&store->monitor_index, r->pool, slcf->monitor->nelts, 795 | sizeof(ngx_http_status_rbnode_t *)) == NGX_ERROR) 796 | { 797 | return NULL; 798 | } 799 | 800 | shm_zone = slcf->monitor->elts; 801 | for (i = 0; i < slcf->monitor->nelts; i++) { 802 | z = shm_zone[i]; 803 | ctx = z->data; 804 | 805 | if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { 806 | ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, 807 | "failed to reap the key \"%V\"", ctx->val); 808 | continue; 809 | } 810 | 811 | fnode = ngx_http_status_rbtree_lookup(shm_zone[i], &val); 812 | 813 | if (fnode == NULL) { 814 | ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, 815 | "failed to alloc node in zone \"%V\", " 816 | "enlarge it please", 817 | &z->shm.name); 818 | 819 | } else { 820 | fnode_store = ngx_array_push(&store->monitor_index); 821 | *fnode_store = fnode; 822 | } 823 | } 824 | 825 | return store; 826 | } 827 | 828 | static ngx_int_t 829 | ngx_http_status_add_variables(ngx_conf_t *cf) 830 | { 831 | ngx_http_variable_t *var, *v; 832 | 833 | for (v = ngx_http_status_vars; v->name.len != 0; v++) { 834 | var = ngx_http_add_variable(cf, &v->name, v->flags); 835 | if (var == NULL) { 836 | return NGX_ERROR; 837 | } 838 | 839 | var->get_handler = v->get_handler; 840 | var->flags = v->flags; 841 | } 842 | 843 | return NGX_OK; 844 | } 845 | 846 | static ngx_int_t 847 | ngx_http_status_upstream_first_addr_variable(ngx_http_request_t *r, 848 | ngx_http_variable_value_t *v, uintptr_t data) 849 | { 850 | ngx_uint_t i; 851 | ngx_http_upstream_state_t *state; 852 | 853 | v->valid = 1; 854 | v->no_cacheable = 0; 855 | v->not_found = 0; 856 | 857 | if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { 858 | v->not_found = 1; 859 | return NGX_OK; 860 | } 861 | 862 | state = r->upstream_states->elts; 863 | 864 | for (i = 0; i < r->upstream_states->nelts; i++) { 865 | if (state[i].peer) { 866 | v->not_found = 0; 867 | v->len = state[i].peer->len; 868 | v->data = state[i].peer->data; 869 | break; 870 | } else { 871 | v->not_found = 1; 872 | } 873 | } 874 | 875 | return NGX_OK; 876 | } 877 | --------------------------------------------------------------------------------