├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── conf ├── nginx.conf └── nginx_dc.conf ├── config ├── docker-compose.yml ├── influxdb.conf ├── src ├── ngx_http_influx_net.c ├── ngx_http_stat_allocator.c ├── ngx_http_stat_allocator.h ├── ngx_http_stat_array.c ├── ngx_http_stat_array.h ├── ngx_http_stat_module.c └── ngx_http_stat_module.h └── t └── influx_udp_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | test-root 3 | nginx 4 | *.dSYM 5 | *.src.rpm 6 | .DS_Store 7 | *.pyc 8 | *_test 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | MAINTAINER aosviridov@domclick.ru 3 | 4 | RUN apk add --update --no-cache build-base pcre pcre-dev zlib zlib-dev 5 | 6 | ADD . /stat 7 | 8 | RUN wget http://nginx.org/download/nginx-1.9.11.tar.gz && \ 9 | tar xvfz nginx-1.9.11.tar.gz && \ 10 | cd /nginx-1.9.11 && \ 11 | ./configure \ 12 | --sbin-path=/usr/local/nginx/nginx \ 13 | --conf-path=/stat/conf/nginx_dc.conf \ 14 | --pid-path=/usr/local/nginx/nginx.pid \ 15 | --add-dynamic-module=/stat/ && \ 16 | make && \ 17 | make install && \ 18 | mkdir -p /usr/local/nginx/www/static/ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | Copyright (c) 2018 Vasiliy Soshnikov 4 | 5 | This module is licensed under the terms of the BSD license. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = all 2 | 3 | NGX_CONFIGURE = ./auto/configure 4 | ## Some versions of nginx have different path of the configure, 5 | ## following lines are handle it {{ 6 | ifeq ($(shell [ -e "$(NGX_PATH)/configure" ] && echo 1 || echo 0 ), 1) 7 | NGX_CONFIGURE=./configure 8 | endif 9 | ## }} 10 | 11 | NGX_PATH = nginx 12 | PREFIX_PATH = $(PWD)/test-root 13 | MODULE_PATH = $(PWD) 14 | WZ2 = $(MODULE_PATH)/../wz2 15 | PROJ_DIR = $(PWD)/.. 16 | 17 | DEV_CFLAGS += -ggdb3 -O0 -Wall -Werror 18 | 19 | 20 | all: build 21 | 22 | build: 23 | $(MAKE) -C $(NGX_PATH) 24 | 25 | configure-dev: 26 | cd $(NGX_PATH) && \ 27 | CFLAGS="$(DEV_CFLAGS)" $(NGX_CONFIGURE) \ 28 | --with-http_addition_module \ 29 | --prefix=$(PREFIX_PATH) \ 30 | --add-module=$(MODULE_PATH) \ 31 | --with-debug 32 | mkdir -p $(PREFIX_PATH)/conf $(PREFIX_PATH)/logs 33 | cp -Rf $(NGX_PATH)/conf/* $(PREFIX_PATH)/conf 34 | cp -f $(MODULE_PATH)/conf/nginx.conf $(PREFIX_PATH)/conf/nginx.conf 35 | 36 | clean_influx_udp_test: 37 | rm -f influix_udp_test 38 | influx_udp_test: clean_influx_udp_test 39 | gcc -Wall -Werror -g t/influx_udp_test.c -o influx_udp_test 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-stat-module 2 | ------------------- 3 | 4 | A nginx module for collecting location stats into a specified engine. 5 | 6 | This module use shared memory segment to collect aggregated stats from all workers 7 | and send calculated values for last minute to `a specefied engine` every 60s (default) 8 | over UDP or TCP in non-blocking way. 9 | 10 | Stats aggegation made on the fly in fixed size buffer allocated on server 11 | start and does't affect server perfomance. 12 | 13 | You can build this module as a dynamic one. 14 | 15 | Some parts are based on [graphite-nginx-module](https://github.com/mailru/graphite-nginx-module) 16 | 17 | *Note. This module is not distributed with the Nginx source.* 18 | 19 | ## Requirements 20 | --------------- 21 | * nginx: 1.2.0 - 1.x.x 22 | 23 | ## Features 24 | ----------- 25 | * Aggregation of location, server or http metrics 26 | * Calculation of percentiles 27 | * Sending data to: 28 | * Influx over UDP or TCP in non-blocking way 29 | 30 | ## Versions 31 | ----------- 32 | * v0.0.1 - beta. 33 | 34 | ## Contents 35 | ----------- 36 | * [Directives](#Directives) 37 | * [stat_config](#stat_config) 38 | * [stat](#stat) 39 | * [stat_default](#stat_default) 40 | * [stat_param](#stat_param) 41 | * [Aggregate functions](aggregate-functions) 42 | * [Params](#params) 43 | * [Percentiles](percentiles) 44 | * [How to install](#how-to-install) 45 | 46 | ## Directives 47 | ------------- 48 | 49 | ## stat_config 50 | -------------- 51 | **syntax:** *stat_config [key1=<value1> key2=<value2> ... keyN=<valueN>]* 52 | 53 | **context:** *http* 54 | 55 | Specify global settings for a whole server instance. 56 | 57 | Param | Required | Default | Description 58 | --------- | -------- | ------------- | ----------- 59 | host | | gethostname() | host name for all tag 60 | server | Yes | | an engine server IP address 61 | protocol | Yes | | an engine type 62 | port | | 2003 | an engine server port 63 | frequency | | 60 | how often send values to the engine 64 | intervals | | 1m | aggregation intervals, time interval list, vertical bar separator (`m` - minutes) 65 | params | | * | limit metrics list to track, vertical bar separator 66 | shared | | 2m | shared memory size, increase in case of `too small shared memory` error 67 | buffer | | 64k | network buffer size, increase in case of `too small buffer size` error 68 | package | | 1400 | maximum UDP packet size 69 | template | | | template for graph name (default is $prefix.$host.$split.$param_$interval) 70 | 71 | Example: 72 | ```nginx 73 | http { 74 | stat_config 75 | protocol=influx/udp 76 | server=127.0.0.1:8089 77 | frequency=1 78 | ; 79 | } 80 | ``` 81 | 82 | [Back to contents](#contents) 83 | 84 | ## stat_default 85 | --------------- 86 | 87 | **syntax:** *stat_default <path prefix> [params=<params>] [if=<condition>]* 88 | 89 | **context:** *http, server* 90 | 91 | Create measurement point in all nested locations. 92 | You can use "$location" variable which represents the name of the current location with all non-alphanumeric characters replaced with "\_." Leading and trailing "\_" are deleted. 93 | 94 | Example: 95 | ```nginx 96 | stat_default nginx.$location; 97 | 98 | location /foo/ { 99 | } 100 | 101 | location /bar/ { 102 | } 103 | ``` 104 | 105 | Data for `/foo/` will be sent to `nginx.foo`, data for `/bar/` - to `nginx.bar`. 106 | The `` parameter (1.3.0) specifies list of params to be collected for all nested locations. To add all default params, use \*. 107 | The `` parameter (1.1.0) enables conditional logging. A request will not be logged if the condition evaluates to "0" or an empty string. 108 | 109 | [Back to contents](#contents) 110 | 111 | ## stat 112 | ------- 113 | **syntax:** *stat <path prefix> [params=<params>] [if=<condition>]* 114 | 115 | **context:** *http, server, location, if* 116 | 117 | Create measurement point in specific location. 118 | 119 | Example: 120 | ```nginx 121 | location /foo/ { 122 | stat nginx.foo; 123 | } 124 | ``` 125 | 126 | The `` parameter (1.3.0) specifies list of params to be collected for this location. To add all default params, use \*. 127 | The `` parameter (1.1.0) enables conditional logging. A request will not be logged if the condition evaluates to "0" or an empty string. 128 | 129 | Example: 130 | ```nginx 131 | map $scheme $is_http { http 1; } 132 | map $scheme $is_https { https 1; } 133 | # ... 134 | location /bar/ { 135 | stat nginx.all.bar; 136 | stat nginx.http.bar if=$is_http; 137 | stat nginx.https.bar if=$is_https; 138 | stat nginx.arg params=rps|request_time; 139 | stat nginx.ext params=*|rps|request_time; 140 | } 141 | ``` 142 | 143 | [Back to contents](#contents) 144 | 145 | ## stat_param 146 | ------------- 147 | **syntax:** *stat_param name=<path> interval=<time value> aggregate=<func>* 148 | 149 | **context:** *location* 150 | 151 | Param | Required | Description 152 | ---------- | -------- | ----------- 153 | name | Yes | path prefix for all graphs 154 | interval | Yes\* | aggregation interval, time intrval value format (`m` - minutes) 155 | aggregate | Yes\* | aggregation function on values 156 | percentile | Yes\* | percentile level 157 | 158 | ## Aggregate functions 159 | ---------------------- 160 | func | Description 161 | ------ | ----------- 162 | sum | sum of values per interval 163 | persec | sum of values per second (`sum` devided on seconds in `interval`) 164 | avg | average value on interval 165 | 166 | ## Params 167 | --------- 168 | Param | Units | Func | Description 169 | ----------------------- | ----- | ---- | ------------------------------------------ 170 | request\_time | ms | avg | total time spent on serving request 171 | bytes\_sent | bytes | avg | http response length 172 | body\_bytes\_sent | bytes | avg | http response body length 173 | request\_length | bytes | avg | http request length 174 | content\_time | ms | avg | time spent generating content inside nginx 175 | upstream\_time | ms | avg | time spent tailking with upstream 176 | upstream\_connect\_time | ms | avg | time spent on upstream connect (nginx >= 1.9.1) 177 | upstream\_header\_time | ms | avg | time spent on upstream header (nginx >= 1.9.1) 178 | rps | rps | sum | total requests number per aggregation interval 179 | keepalive\_rps | rps | sum | requests number sent over previously opened keepalive connection 180 | response\_2xx\_rps | rps | sum | total responses number with 2xx code 181 | response\_3xx\_rps | rps | sum | total responses number with 3xx code 182 | response\_4xx\_rps | rps | sum | total responses number with 4xx code 183 | response\_5xx\_rps | rps | sum | total responses number with 5xx code 184 | response\_[0-9]{3}\_rps | rps | sum | total responses number with given code 185 | upstream\_cache\_(miss\|bypass\|expired\|stale\|updating\|revalidated\|hit)\_rps | rps | sum | totar responses with a given upstream cache status 186 | 187 | [Back to contents](#contents) 188 | 189 | ## Percentiles 190 | -------------- 191 | To calculate percentile value for any parameter, set percentile level via `/`. E.g. `request_time/50|request_time/90|request_time/99`. 192 | 193 | [Back to contents](#contents) 194 | 195 | ## How to install 196 | ----------------- 197 | 198 | ### Dynamic module: 199 | ------------------- 200 | ```bash 201 | $> wget 'http://nginx.org/download/nginx-1.9.11.tar.gz' 202 | $> tar -xzf nginx-1.9.11.tar.gz 203 | $> cd nginx-1.9.11/ 204 | $> ./configure --add-dynamic-module=/path/to/nginx-stat-module 205 | $> make 206 | $> make install 207 | ``` 208 | ### Static module: 209 | ------------------ 210 | ```bash 211 | $> wget 'http://nginx.org/download/nginx-1.9.11.tar.gz' 212 | $> tar -xzf nginx-1.9.11.tar.gz 213 | $> cd nginx-1.9.11/ 214 | $> ./configure --add-module=/path/to/nginx-stat-module 215 | $> make 216 | $> make install 217 | ``` 218 | 219 | [Back to contents](#contents) 220 | 221 | -------------------------------------------------------------------------------- /conf/nginx.conf: -------------------------------------------------------------------------------- 1 | load_module modules/ngx_http_stat_module.so; 2 | 3 | daemon off; 4 | master_process off; 5 | 6 | pid logs/nginx.pid; 7 | 8 | error_log logs/notice.log notice; 9 | error_log logs/info.log info; 10 | error_log logs/crit.log crit; 11 | error_log logs/debug.log debug; 12 | error_log stderr; 13 | 14 | 15 | events {} 16 | 17 | http { 18 | 19 | root www; 20 | 21 | access_log off; 22 | 23 | include mime.types; 24 | default_type application/octet-stream; 25 | 26 | stat_config 27 | protocol=influx/udp 28 | server=127.0.0.1:8089 29 | frequency=1; 30 | 31 | server { 32 | listen 8081 default; 33 | server_name t; 34 | 35 | location /static { 36 | stat nginx.all params=*; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /conf/nginx_dc.conf: -------------------------------------------------------------------------------- 1 | load_module modules/ngx_http_stat_module.so; 2 | 3 | daemon off; 4 | master_process off; 5 | 6 | pid logs/nginx.pid; 7 | 8 | error_log logs/notice.log notice; 9 | error_log logs/info.log info; 10 | error_log logs/crit.log crit; 11 | error_log logs/debug.log debug; 12 | error_log stderr; 13 | 14 | 15 | events {} 16 | 17 | http { 18 | 19 | root www; 20 | 21 | access_log off; 22 | 23 | include mime.types; 24 | default_type application/octet-stream; 25 | 26 | stat_config 27 | protocol=influx/udp 28 | server=localhost:8090 29 | frequency=1; 30 | 31 | server { 32 | listen 8081 default; 33 | server_name t; 34 | 35 | location /static { 36 | stat nginx.all params=*; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | # (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | # 6 | 7 | ngx_feature="Stat module" 8 | 9 | have=NGX_STAT_MODULE . auto/have 10 | have=NGX_STAT_INFLUX . auto/have 11 | 12 | ngx_addon_name=ngx_http_influx_module 13 | 14 | ngx_module_type=HTTP 15 | ngx_module_name=ngx_http_stat_module 16 | ngx_module_incs="$ngx_addon_dir/src" 17 | ngx_module_srcs="\ 18 | $ngx_addon_dir/src/ngx_http_stat_allocator.c\ 19 | $ngx_addon_dir/src/ngx_http_stat_array.c\ 20 | $ngx_addon_dir/src/ngx_http_stat_module.c\ 21 | $ngx_addon_dir/src/ngx_http_influx_net.c\ 22 | " 23 | . auto/module 24 | 25 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | nginx: 5 | build: . 6 | command: /usr/local/nginx/nginx 7 | ports: 8 | - "8081:8081" 9 | links: 10 | - influxdb 11 | influxdb: 12 | image: influxdb:latest 13 | container_name: influxdb 14 | ports: 15 | - "8083:8083" 16 | - "8086:8086" 17 | - "8090:8090" 18 | volumes: 19 | - ./influxdb_data:/var/lib/influxdb 20 | - ./influxdb.conf:/etc/influxdb/influxdb.conf 21 | environment: 22 | - INFLUXDB_DATA_ENGINE=tsm1 23 | - INFLUXDB_REPORTING_DISABLED=false 24 | -------------------------------------------------------------------------------- /influxdb.conf: -------------------------------------------------------------------------------- 1 | [meta] 2 | dir = "/var/lib/influxdb/meta" 3 | 4 | [data] 5 | dir = "/var/lib/influxdb/data" 6 | engine = "tsm1" 7 | wal-dir = "/var/lib/influxdb/wal" 8 | 9 | [[udp]] 10 | # Default UDP for Telegraf 11 | enabled = true 12 | bind-address = ":8090" # the bind address 13 | database = "nginx" # Name of the database that will be written to 14 | batch-size = 5000 # will flush if this many points get buffered 15 | batch-timeout = "1s" # will flush at least this often even if the batch-size is not reached 16 | batch-pending = 10 # number of batches that may be pending in memory 17 | read-buffer = 0 # UDP read buffer size, 0 means to use OS default 18 | presision = "s" # sets the default data presision to seconds -------------------------------------------------------------------------------- /src/ngx_http_influx_net.c: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #include "ngx_http_stat_module.h" 8 | 9 | 10 | static ngx_int_t ngx_http_influx_net_send_udp( 11 | ngx_http_stat_main_conf_t *smcf, ngx_log_t *log); 12 | static ngx_int_t ngx_http_influx_net_connect_udp( 13 | ngx_http_stat_main_conf_t *smcf, ngx_log_t *log); 14 | static u_char *ngx_http_influx_s11n_metric(ngx_http_stat_main_conf_t *smcf, 15 | ngx_http_stat_storage_t *storage, ngx_uint_t m, 16 | ngx_http_stat_interval_t *interval, time_t ts, 17 | u_char *buffer, ngx_uint_t buffer_size); 18 | static u_char *ngx_http_influx_s11n_statistic(ngx_http_stat_main_conf_t *smcf, 19 | ngx_http_stat_storage_t *storage, ngx_uint_t s, time_t ts, 20 | u_char *buffer, ngx_uint_t buffer_size); 21 | 22 | 23 | void 24 | ngx_http_influx_udp_timer_handler(ngx_event_t *ev) 25 | { 26 | time_t ts; 27 | ngx_http_stat_main_conf_t *smcf; 28 | ngx_buf_t *buffer; 29 | u_char *b; 30 | ngx_slab_pool_t *shpool; 31 | ngx_http_stat_storage_t *storage; 32 | ngx_uint_t m, i, s; 33 | ngx_http_stat_metric_t *metric; 34 | ngx_http_stat_param_t *param; 35 | ngx_http_stat_interval_t *interval; 36 | ngx_http_stat_statistic_t *statistic; 37 | 38 | smcf = ev->data; 39 | 40 | buffer = &smcf->buffer; 41 | b = buffer->start; 42 | 43 | shpool = (ngx_slab_pool_t *) smcf->shared->shm.addr; 44 | storage = (ngx_http_stat_storage_t *) shpool->data; 45 | 46 | ts = ngx_time(); 47 | 48 | /** Lock {{{ */ 49 | ngx_shmtx_lock(&shpool->mutex); 50 | 51 | if ((ngx_uint_t) (ts - storage->event_time) * 1000 < smcf->frequency) { 52 | ngx_shmtx_unlock(&shpool->mutex); 53 | goto yeild; 54 | } 55 | 56 | if (smcf->connection) { 57 | ngx_log_error(NGX_LOG_NOTICE, ev->log, 0, "re-init connection"); 58 | ngx_close_connection(smcf->connection); 59 | smcf->connection = NULL; 60 | } 61 | 62 | if (storage->allocator->nomemory) { 63 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 64 | "shared memory is full"); 65 | } 66 | 67 | storage->event_time = ts; 68 | 69 | ngx_http_stat_gc(smcf, storage->event_time); 70 | 71 | for (m = 0; m < storage->metrics->nelts; m++) { 72 | 73 | metric = &((ngx_http_stat_metric_t *) storage->metrics->elts)[m]; 74 | param = &((ngx_http_stat_param_t *) 75 | storage->params->elts)[metric->param]; 76 | 77 | if (metric->split != SPLIT_INTERNAL) { 78 | 79 | for (i = 0; i < smcf->intervals->nelts; i++) { 80 | 81 | interval = &((ngx_http_stat_interval_t *) 82 | smcf->intervals->elts)[i]; 83 | 84 | b = ngx_http_influx_s11n_metric(smcf, storage, m, 85 | interval, storage->event_time, b, 86 | smcf->buffer_size - (b - buffer->start)); 87 | } 88 | 89 | } else { 90 | 91 | b = ngx_http_influx_s11n_metric(smcf, storage, m, 92 | ¶m->interval, storage->event_time, b, 93 | smcf->buffer_size - (b - buffer->start)); 94 | 95 | } 96 | 97 | } 98 | 99 | for (s = 0; s < storage->statistics->nelts; s++) { 100 | 101 | statistic = &((ngx_http_stat_statistic_t *) 102 | storage->statistics->elts)[s]; 103 | 104 | param = &((ngx_http_stat_param_t *) 105 | storage->params->elts)[statistic->param]; 106 | 107 | b = ngx_http_influx_s11n_statistic(smcf, storage, s, 108 | storage->event_time, b, 109 | smcf->buffer_size - (b - buffer->start)); 110 | 111 | ngx_http_stat_statistic_init(statistic->stt, param->percentile); 112 | } 113 | 114 | *b = '\0'; 115 | 116 | ngx_shmtx_unlock(&shpool->mutex); 117 | 118 | /** Lock }}} */ 119 | 120 | if (b == buffer->start + smcf->buffer_size) { 121 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 122 | "stat buffer size is too small"); 123 | goto yeild; 124 | } 125 | 126 | if (b != buffer->start) { 127 | 128 | buffer->pos = buffer->start; 129 | buffer->last = b; 130 | 131 | ngx_http_influx_net_send_udp(smcf, ev->log); 132 | } 133 | 134 | yeild: 135 | if (ngx_quit || ngx_terminate || ngx_exiting) { 136 | return; 137 | } 138 | 139 | ngx_time_update(); 140 | 141 | ngx_add_timer(ev, smcf->frequency); 142 | } 143 | 144 | 145 | static 146 | ngx_int_t 147 | ngx_http_influx_net_send_udp(ngx_http_stat_main_conf_t *smcf, 148 | ngx_log_t *log) 149 | { 150 | ngx_buf_t *b; 151 | u_char *part, *next, *nl; 152 | ssize_t n; 153 | 154 | if (smcf->connection) { 155 | return NGX_ERROR; 156 | } 157 | 158 | if (ngx_http_influx_net_connect_udp(smcf, log) != NGX_OK) { 159 | ngx_log_error(NGX_LOG_ERR, log, 0, 160 | "ngx_http_influx_net_connect_udp: connect to \"%V\" failed", 161 | &smcf->server.name); 162 | goto failed; 163 | } 164 | 165 | smcf->connection->data = smcf; 166 | 167 | b = &smcf->buffer; 168 | 169 | part = b->start; 170 | next = NULL; 171 | nl = NULL; 172 | 173 | while (*part) { 174 | 175 | next = part; 176 | nl = part; 177 | 178 | while ((next = (u_char*) ngx_strchr(next, '\n')) && 179 | ((size_t) (next - part) <= smcf->package_size)) 180 | { 181 | nl = next; 182 | next++; 183 | } 184 | 185 | if (nl > part) { 186 | 187 | n = ngx_send(smcf->connection, part, nl - part + 1); 188 | 189 | if (n == -1) { 190 | ngx_log_error(NGX_LOG_ERR, log, n, 191 | "ngx_http_influx_net_connect_udp: " 192 | "udp send to \"%V\" error", 193 | &smcf->server.name); 194 | goto failed; 195 | } 196 | 197 | if (n != nl - part + 1) { 198 | ngx_log_error(NGX_LOG_ERR, log, 0, 199 | "ngx_http_influx_net_connect_udp: udp send to \"%V\" " 200 | "incomplete", &smcf->server.name); 201 | goto failed; 202 | } 203 | } 204 | else { 205 | ngx_log_error(NGX_LOG_ERR, log, 0, 206 | "ngx_http_influx_net_connect_udp: package size too small, " 207 | "need send %z to \"%V\"", 208 | (size_t) (next - part), &smcf->server.name); 209 | } 210 | 211 | part = nl + 1; 212 | } 213 | 214 | ngx_close_connection(smcf->connection); 215 | smcf->connection = NULL; 216 | 217 | return NGX_OK; 218 | 219 | failed: 220 | 221 | ngx_close_connection(smcf->connection); 222 | smcf->connection = NULL; 223 | 224 | return NGX_ERROR; 225 | } 226 | 227 | 228 | static ngx_int_t 229 | ngx_http_influx_net_connect_udp(ngx_http_stat_main_conf_t *smcf, 230 | ngx_log_t *log) 231 | { 232 | ngx_int_t rc, event; 233 | ngx_socket_t s; 234 | ngx_connection_t *c; 235 | ngx_event_t *rev, *wev; 236 | 237 | s = ngx_socket(smcf->server.sockaddr->sa_family, SOCK_DGRAM, 0); 238 | 239 | if (s == (ngx_socket_t) -1) { 240 | ngx_log_error(NGX_LOG_ERR, log, ngx_socket_errno, 241 | "ngx_http_influx_net_connect_udp: " ngx_socket_n " failed"); 242 | return NGX_ERROR; 243 | } 244 | 245 | c = ngx_get_connection(s, log); 246 | 247 | if (c == NULL) { 248 | if (ngx_close_socket(s) == -1) { 249 | ngx_log_error(NGX_LOG_ERR, log, ngx_socket_errno, 250 | "ngx_http_influx_net_connect_udp: " ngx_close_socket_n 251 | " failed"); 252 | } 253 | 254 | return NGX_ERROR; 255 | } 256 | 257 | rev = c->read; 258 | wev = c->write; 259 | 260 | rev->log = log; 261 | wev->log = log; 262 | 263 | smcf->connection = c; 264 | 265 | c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); 266 | 267 | rc = connect(s, smcf->server.sockaddr, smcf->server.socklen); 268 | 269 | if (rc == -1) { 270 | ngx_log_error(NGX_LOG_ERR, log, ngx_socket_errno, 271 | "ngx_http_influx_net_connect_udp: connect failed"); 272 | goto failed; 273 | } 274 | 275 | wev->ready = 1; 276 | 277 | event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? 278 | NGX_CLEAR_EVENT: NGX_LEVEL_EVENT; 279 | 280 | if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { 281 | goto failed; 282 | } 283 | 284 | return NGX_OK; 285 | 286 | failed: 287 | 288 | ngx_close_connection(c); 289 | 290 | return NGX_ERROR; 291 | } 292 | /** }}} */ 293 | 294 | /** Serializer part 295 | */ 296 | static u_char * 297 | ngx_http_influx_s11n_metric(ngx_http_stat_main_conf_t *smcf, 298 | ngx_http_stat_storage_t *storage, ngx_uint_t m, 299 | ngx_http_stat_interval_t *interval, time_t ts, 300 | u_char *buffer, ngx_uint_t buffer_size) 301 | { 302 | ngx_uint_t l, a; 303 | double value; 304 | ngx_http_stat_metric_t *metric; 305 | ngx_http_stat_param_t *param; 306 | ngx_http_stat_acc_t aggregate, *acc; 307 | u_char *b; 308 | ngx_str_t *split; 309 | 310 | metric = &((ngx_http_stat_metric_t *) storage->metrics->elts)[m]; 311 | param = &((ngx_http_stat_param_t *) storage->params->elts)[metric->param]; 312 | 313 | if (metric->acc == NULL) { 314 | return buffer; 315 | } 316 | 317 | aggregate.value = 0; 318 | aggregate.count = 0; 319 | 320 | for (l = 0; l < interval->value; l++) { 321 | 322 | if ((time_t) (ts - l - 1) >= storage->start_time) { 323 | a = (ts - l - 1 - storage->start_time) % 324 | (storage->max_interval + 1); 325 | acc = &metric->acc[a]; 326 | aggregate.value += acc->value; 327 | aggregate.count += acc->count; 328 | } 329 | } 330 | 331 | value = param->aggregate(interval, &aggregate); 332 | 333 | b = buffer; 334 | 335 | if (metric->split != SPLIT_INTERNAL) { 336 | 337 | split = &((ngx_str_t *) smcf->splits->elts)[metric->split]; 338 | 339 | if (!smcf->template->nelts) { 340 | 341 | b = ngx_snprintf(b, buffer_size - (b - buffer), 342 | "%V,location=%V,parameter=%V,interval=%V", 343 | &smcf->host, split, ¶m->name, &interval->name); 344 | 345 | } else { 346 | 347 | ngx_str_t *variables[] = TEMPLATE_VARIABLES( 348 | &smcf->host, split, ¶m->name, &interval->name); 349 | 350 | b = ngx_http_stat_template_execute(b, buffer_size - (b - buffer), 351 | smcf->template, variables); 352 | } 353 | 354 | } else { 355 | 356 | b = ngx_snprintf(b, buffer_size - (b - buffer), "%V,parameter=%V", 357 | &smcf->host, ¶m->name); 358 | 359 | } 360 | 361 | b = ngx_snprintf(b, buffer_size - (b - buffer), " value=%.3f %T\n", 362 | value, ts); 363 | 364 | return b; 365 | } 366 | 367 | 368 | static u_char * 369 | ngx_http_influx_s11n_statistic(ngx_http_stat_main_conf_t *smcf, 370 | ngx_http_stat_storage_t *storage, ngx_uint_t s, time_t ts, 371 | u_char *buffer, ngx_uint_t buffer_size) 372 | { 373 | ngx_http_stat_statistic_t *statistic; 374 | ngx_http_stat_param_t *param; 375 | ngx_http_stat_stt_t *stt; 376 | u_char p[4], *b; 377 | ngx_str_t percentile, *split; 378 | 379 | statistic = &((ngx_http_stat_statistic_t *) storage->statistics->elts)[s]; 380 | param = &((ngx_http_stat_param_t *) storage->params->elts)[statistic->param]; 381 | 382 | if (statistic->stt == NULL) { 383 | return buffer; 384 | } 385 | 386 | percentile.data = p; 387 | percentile.len = ngx_snprintf(p, sizeof(p), "p%ui", 388 | param->percentile) - p; 389 | 390 | stt = statistic->stt; 391 | 392 | b = buffer; 393 | 394 | if (statistic->split != SPLIT_INTERNAL) { 395 | 396 | split = &((ngx_str_t *) smcf->splits->elts)[statistic->split]; 397 | 398 | if (smcf->template->nelts == 0) { 399 | 400 | b = ngx_snprintf(b, buffer_size - (b - buffer), 401 | "%V,location=%V,parameter=%V,percentile=%V", 402 | &smcf->host, split, ¶m->name, &percentile); 403 | } else { 404 | ngx_str_t *variables[] = TEMPLATE_VARIABLES( 405 | &smcf->host, split, ¶m->name, &percentile); 406 | b = ngx_http_stat_template_execute(b, buffer_size - (b - buffer), 407 | smcf->template, variables); 408 | } 409 | } else { 410 | b = ngx_snprintf(b, buffer_size - (b - buffer), 411 | "%V,parameter=%V,percentile=%V", 412 | &smcf->host, ¶m->name, &percentile); 413 | } 414 | 415 | b = ngx_snprintf(b, buffer_size - (b - buffer), " value=%.3f %T\n", 416 | stt->q[P2_METRIC_COUNT / 2], ts); 417 | 418 | return b; 419 | } 420 | /** }}} */ 421 | 422 | -------------------------------------------------------------------------------- /src/ngx_http_stat_allocator.c: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #include "ngx_http_stat_allocator.h" 8 | 9 | 10 | void 11 | ngx_http_stat_allocator_init(ngx_http_stat_allocator_t *allocator, 12 | void *pool, void* (*alloc)(void *, size_t), 13 | void (*free)(void *, void *)) 14 | { 15 | allocator->pool = pool; 16 | allocator->alloc = alloc; 17 | allocator->free = free; 18 | allocator->nomemory = 0; 19 | } 20 | 21 | 22 | void * 23 | ngx_http_stat_allocator_pool_alloc(void *pool, size_t size) 24 | { 25 | return ngx_palloc((ngx_pool_t*) pool, size); 26 | } 27 | 28 | 29 | void 30 | ngx_http_stat_allocator_pool_free(void *pool, void *p) 31 | { 32 | if (p) { 33 | ngx_pfree((ngx_pool_t*) pool, p); 34 | } 35 | } 36 | 37 | void * 38 | ngx_http_stat_allocator_slab_alloc(void *pool, size_t size) 39 | { 40 | return ngx_slab_alloc_locked((ngx_slab_pool_t*) pool, size); 41 | } 42 | 43 | 44 | void 45 | ngx_http_stat_allocator_slab_free(void *pool, void *p) 46 | { 47 | if (p) { 48 | ngx_slab_free_locked((ngx_slab_pool_t*)pool, p); 49 | } 50 | } 51 | 52 | 53 | void * 54 | ngx_http_stat_allocator_alloc(ngx_http_stat_allocator_t *allocator, 55 | size_t size) 56 | { 57 | void *p; 58 | p = allocator->alloc(allocator->pool, size); 59 | if (p == NULL) { 60 | allocator->nomemory = 1; 61 | } 62 | return p; 63 | } 64 | 65 | 66 | void 67 | ngx_http_stat_allocator_free(ngx_http_stat_allocator_t *allocator, 68 | void *p) 69 | { 70 | if (p) { 71 | allocator->free(allocator->pool, p); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/ngx_http_stat_allocator.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #ifndef NGX_HTTP_STAT_ALLOCATOR_H_INCLUDED 8 | #define NGX_HTTP_STAT_ALLOCATOR_H_INCLUDED 1 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | typedef struct { 17 | void *pool; 18 | void *(*alloc)(void *pool, size_t size); 19 | void (*free)(void *pool, void *p); 20 | ngx_int_t nomemory; 21 | } ngx_http_stat_allocator_t; 22 | 23 | 24 | void ngx_http_stat_allocator_init(ngx_http_stat_allocator_t *allocator, 25 | void *pool, void* (*alloc)(void *, size_t), void (*free)(void *, void *)); 26 | void *ngx_http_stat_allocator_pool_alloc(void *pool, size_t size); 27 | void ngx_http_stat_allocator_pool_free(void *pool, void *p); 28 | void *ngx_http_stat_allocator_slab_alloc(void *pool, size_t size); 29 | void ngx_http_stat_allocator_slab_free(void *pool, void *p); 30 | 31 | void *ngx_http_stat_allocator_alloc(ngx_http_stat_allocator_t *allocator, 32 | size_t size); 33 | void ngx_http_stat_allocator_free(ngx_http_stat_allocator_t *allocator, 34 | void *p); 35 | 36 | #endif /** NGX_HTTP_STAT_ALLOCATOR_H_INCLUDED */ 37 | 38 | -------------------------------------------------------------------------------- /src/ngx_http_stat_array.c: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #include "ngx_http_stat_array.h" 8 | 9 | 10 | ngx_http_stat_array_t * 11 | ngx_http_stat_array_create(ngx_http_stat_allocator_t *allocator, 12 | ngx_uint_t n, size_t size) 13 | { 14 | ngx_http_stat_array_t *array; 15 | 16 | array = ngx_http_stat_allocator_alloc(allocator, 17 | sizeof(ngx_http_stat_array_t)); 18 | if (array == NULL) { 19 | return NULL; 20 | } 21 | 22 | if (ngx_http_stat_array_init(array, allocator, n, size) != NGX_OK) { 23 | return NULL; 24 | } 25 | 26 | return array; 27 | } 28 | 29 | 30 | ngx_int_t 31 | ngx_http_stat_array_init(ngx_http_stat_array_t *array, 32 | ngx_http_stat_allocator_t *allocator, ngx_uint_t n, 33 | size_t size) 34 | { 35 | array->nelts = 0; 36 | array->size = size; 37 | array->nalloc = n; 38 | array->allocator = allocator; 39 | 40 | array->elts = ngx_http_stat_allocator_alloc(allocator, n * size); 41 | if (array->elts == NULL) { 42 | return NGX_ERROR; 43 | } 44 | 45 | return NGX_OK; 46 | } 47 | 48 | 49 | void 50 | ngx_http_stat_array_destroy(ngx_http_stat_array_t *array) 51 | { 52 | ngx_http_stat_allocator_t *allocator; 53 | 54 | allocator = array->allocator; 55 | 56 | ngx_http_stat_allocator_free(allocator, array->elts); 57 | ngx_http_stat_allocator_free(allocator, array); 58 | } 59 | 60 | 61 | void * 62 | ngx_http_stat_array_push(ngx_http_stat_array_t *a) 63 | { 64 | return ngx_http_stat_array_push_n(a, 1); 65 | } 66 | 67 | void * 68 | ngx_http_stat_array_push_n(ngx_http_stat_array_t *array, ngx_uint_t n) 69 | { 70 | ngx_http_stat_allocator_t *allocator; 71 | ngx_uint_t nalloc; 72 | void *new, *elt; 73 | 74 | allocator = array->allocator; 75 | 76 | if (array->nelts == array->nalloc) { 77 | 78 | nalloc = 2 * ((n >= array->nalloc) ? n : array->nalloc); 79 | 80 | new = ngx_http_stat_allocator_alloc(allocator, nalloc * array->size); 81 | if (new == NULL) { 82 | return NULL; 83 | } 84 | 85 | ngx_memcpy(new, array->elts, array->nelts * array->size); 86 | ngx_http_stat_allocator_free(allocator, array->elts); 87 | array->elts = new; 88 | array->nalloc = nalloc; 89 | } 90 | 91 | elt = (u_char* )array->elts + array->size * array->nelts; 92 | array->nelts += n; 93 | 94 | return elt; 95 | } 96 | 97 | ngx_http_stat_array_t * 98 | ngx_http_stat_array_copy(ngx_http_stat_allocator_t *allocator, 99 | ngx_http_stat_array_t *array) 100 | { 101 | ngx_http_stat_array_t *copy; 102 | 103 | copy = ngx_http_stat_array_create(allocator, array->nalloc, array->size); 104 | 105 | if (copy == NULL) { 106 | return NULL; 107 | } 108 | 109 | if (ngx_http_stat_array_push_n(copy, array->nelts) == NULL) { 110 | return NULL; 111 | } 112 | 113 | ngx_memcpy(copy->elts, array->elts, array->nelts * array->size); 114 | 115 | return copy; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/ngx_http_stat_array.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #ifndef NGX_HTTP_STAT_ARRAY_H_INCLUDED 8 | #define NGX_HTTP_STAT_ARRAY_H_INCLUDED 1 9 | 10 | #include "ngx_http_stat_allocator.h" 11 | 12 | 13 | typedef struct { 14 | void *elts; 15 | ngx_uint_t nelts; 16 | size_t size; 17 | ngx_uint_t nalloc; 18 | ngx_http_stat_allocator_t *allocator; 19 | } ngx_http_stat_array_t; 20 | 21 | 22 | ngx_http_stat_array_t *ngx_http_stat_array_create( 23 | ngx_http_stat_allocator_t *allocator, ngx_uint_t n, size_t size); 24 | ngx_int_t ngx_http_stat_array_init(ngx_http_stat_array_t *array, 25 | ngx_http_stat_allocator_t *allocator, ngx_uint_t n, size_t size); 26 | void ngx_http_stat_array_destroy(ngx_http_stat_array_t *array); 27 | void *ngx_http_stat_array_push(ngx_http_stat_array_t *a); 28 | void *ngx_http_stat_array_push_n(ngx_http_stat_array_t *a, ngx_uint_t n); 29 | ngx_http_stat_array_t *ngx_http_stat_array_copy( 30 | ngx_http_stat_allocator_t *allocator, ngx_http_stat_array_t *array); 31 | 32 | #endif /** NGX_HTTP_STAT_ARRAY_H_INCLUDED */ 33 | 34 | -------------------------------------------------------------------------------- /src/ngx_http_stat_module.c: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #include "ngx_http_stat_module.h" 8 | 9 | 10 | /** FWD {{{ */ 11 | typedef char *(*ngx_http_stat_arg_handler_pt)(ngx_http_stat_ctx_t *, 12 | void *, ngx_str_t *); 13 | 14 | typedef struct { 15 | ngx_str_t name; 16 | ngx_http_stat_arg_handler_pt handler; 17 | ngx_str_t deflt; 18 | } ngx_http_stat_arg_t; 19 | 20 | /** Sources (i.e. locations or...) {{{ */ 21 | struct ngx_http_stat_source_s; 22 | 23 | typedef double (*ngx_http_stat_source_handler_pt)( 24 | struct ngx_http_stat_source_s *source, ngx_http_request_t*); 25 | 26 | typedef struct ngx_http_stat_source_s { 27 | ngx_str_t name; 28 | ngx_flag_t re; 29 | ngx_http_stat_source_handler_pt get; 30 | ngx_http_stat_aggregate_pt aggregate; 31 | ngx_uint_t type; 32 | } ngx_http_stat_source_t; 33 | 34 | static double ngx_http_stat_source_request_time( 35 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 36 | static double ngx_http_stat_source_bytes_sent( 37 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 38 | static double ngx_http_stat_source_body_bytes_sent( 39 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 40 | static double ngx_http_stat_source_request_length( 41 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 42 | static double ngx_http_stat_source_rps( 43 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 44 | static double ngx_http_stat_source_keepalive_rps( 45 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 46 | static double ngx_http_stat_source_response_2xx_rps( 47 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 48 | static double ngx_http_stat_source_response_3xx_rps( 49 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 50 | static double ngx_http_stat_source_response_4xx_rps( 51 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 52 | static double ngx_http_stat_source_response_5xx_rps( 53 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 54 | static double ngx_http_stat_source_response_xxx_rps( 55 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 56 | static double ngx_http_stat_source_upstream_cache_status_rps( 57 | ngx_http_stat_source_t *source, ngx_http_request_t *r); 58 | /** }}} */ 59 | 60 | typedef enum { 61 | TEMPLATE_VARIABLE_LOCATION, 62 | } ngx_http_stat_default_data_variable_t; 63 | 64 | static ngx_http_complex_value_t *ngx_http_stat_complex_compile( 65 | ngx_conf_t *cf, ngx_str_t *value); 66 | 67 | static ngx_int_t ngx_http_stat_handler(ngx_http_request_t *r); 68 | static ngx_int_t ngx_http_stat_shared_init(ngx_shm_zone_t *shm_zone, 69 | void *data); 70 | 71 | static ngx_int_t ngx_http_stat_add_variables(ngx_conf_t *cf); 72 | static ngx_int_t ngx_http_stat_init(ngx_conf_t *cf); 73 | static ngx_int_t ngx_http_stat_process_init(ngx_cycle_t *cycle); 74 | 75 | static void *ngx_http_stat_create_main_conf(ngx_conf_t *cf); 76 | static void *ngx_http_stat_create_srv_conf(ngx_conf_t *cf); 77 | static void *ngx_http_stat_create_loc_conf(ngx_conf_t *cf); 78 | static char *ngx_http_stat_merge_loc_conf(ngx_conf_t* cf, void* parent, 79 | void* child); 80 | 81 | static ngx_str_t *ngx_http_stat_location(ngx_pool_t *pool, ngx_str_t *uri); 82 | 83 | static char *ngx_http_stat_config(ngx_conf_t *cf, ngx_command_t *cmd, 84 | void *conf); 85 | static char *ngx_http_stat_default_data(ngx_conf_t *cf, ngx_command_t *cmd, 86 | void *conf); 87 | static char *ngx_http_stat_data(ngx_conf_t *cf, ngx_command_t *cmd, 88 | void *conf); 89 | static char *ngx_http_stat_param(ngx_conf_t *cf, ngx_command_t *cmd, 90 | void *conf); 91 | 92 | static char *ngx_http_stat_add_default_data(ngx_conf_t *cf, ngx_array_t *datas, 93 | ngx_str_t *location, ngx_array_t *template, ngx_array_t *params, 94 | ngx_http_complex_value_t *filter); 95 | static char *ngx_http_stat_add_data(ngx_conf_t *cf, ngx_array_t *datas, 96 | ngx_str_t *split, ngx_array_t *params, ngx_http_complex_value_t *filter); 97 | 98 | static char *ngx_http_stat_config_arg_host(ngx_http_stat_ctx_t *ctx, 99 | void *data, ngx_str_t *value); 100 | static char *ngx_http_stat_config_arg_server(ngx_http_stat_ctx_t *ctx, 101 | void *data, ngx_str_t *value); 102 | static char *ngx_http_stat_config_arg_port(ngx_http_stat_ctx_t *ctx, 103 | void *data, ngx_str_t *value); 104 | static char *ngx_http_stat_config_arg_frequency(ngx_http_stat_ctx_t *ctx, 105 | void *data, ngx_str_t *value); 106 | static char *ngx_http_stat_config_arg_intervals(ngx_http_stat_ctx_t *ctx, 107 | void *data, ngx_str_t *value); 108 | static char *ngx_http_stat_config_arg_params(ngx_http_stat_ctx_t *ctx, 109 | void *data, ngx_str_t *value); 110 | static char *ngx_http_stat_config_arg_shared(ngx_http_stat_ctx_t *ctx, 111 | void *data, ngx_str_t *value); 112 | static char *ngx_http_stat_config_arg_buffer(ngx_http_stat_ctx_t *ctx, 113 | void *data, ngx_str_t *value); 114 | static char *ngx_http_stat_config_arg_package(ngx_http_stat_ctx_t *ctx, 115 | void *data, ngx_str_t *value); 116 | static char *ngx_http_stat_config_arg_template(ngx_http_stat_ctx_t *ctx, 117 | void *data, ngx_str_t *value); 118 | static char *ngx_http_stat_config_arg_protocol(ngx_http_stat_ctx_t *ctx, 119 | void *data, ngx_str_t *value); 120 | static char *ngx_http_stat_config_arg_timeout(ngx_http_stat_ctx_t *ctx, 121 | void *data, ngx_str_t *value); 122 | 123 | static char *ngx_http_stat_param_arg_name(ngx_http_stat_ctx_t *ctx, 124 | void *data, ngx_str_t *value); 125 | static char *ngx_http_stat_param_arg_aggregate(ngx_http_stat_ctx_t *ctx, 126 | void *data, ngx_str_t *value); 127 | static char *ngx_http_stat_param_arg_interval(ngx_http_stat_ctx_t *ctx, 128 | void *data, ngx_str_t *value); 129 | static char *ngx_http_stat_param_arg_percentile(ngx_http_stat_ctx_t *ctx, 130 | void *data, ngx_str_t *value); 131 | 132 | static char *ngx_http_stat_parse_params(ngx_http_stat_ctx_t *ctx, 133 | ngx_str_t *value, ngx_array_t *params); 134 | static char *ngx_http_stat_parse_string(ngx_http_stat_ctx_t *ctx, 135 | ngx_str_t *value, ngx_str_t *result); 136 | static char *ngx_http_stat_parse_size(ngx_http_stat_ctx_t *ctx, 137 | ngx_str_t *value, ngx_uint_t *result); 138 | static char *ngx_http_stat_parse_time(ngx_http_stat_ctx_t *ctx, 139 | ngx_str_t *value, ngx_uint_t *result); 140 | /** }}} */ 141 | 142 | /** Vars {{{ */ 143 | static ngx_command_t ngx_http_stat_commands[] = { 144 | 145 | { ngx_string("stat_config"), 146 | NGX_HTTP_MAIN_CONF|NGX_CONF_ANY, 147 | ngx_http_stat_config, 148 | NGX_HTTP_MAIN_CONF_OFFSET, 149 | 0, 150 | NULL }, 151 | 152 | { ngx_string("stat_param"), 153 | NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE2|NGX_CONF_TAKE3| 154 | NGX_CONF_TAKE4, 155 | ngx_http_stat_param, 156 | NGX_HTTP_LOC_CONF_OFFSET, 157 | 0, 158 | NULL }, 159 | 160 | { ngx_string("stat_default"), 161 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12, 162 | ngx_http_stat_default_data, 163 | 0, 164 | 0, 165 | NULL }, 166 | 167 | { ngx_string("stat"), 168 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF| 169 | NGX_CONF_TAKE123, 170 | ngx_http_stat_data, 171 | 0, 172 | 0, 173 | NULL }, 174 | 175 | ngx_null_command 176 | }; 177 | 178 | 179 | static ngx_http_module_t ngx_http_stat_module_ctx = { 180 | ngx_http_stat_add_variables, /* preconfiguration */ 181 | ngx_http_stat_init, /* postconfiguration */ 182 | 183 | ngx_http_stat_create_main_conf, /* create main configuration */ 184 | NULL, /* init main configuration */ 185 | 186 | ngx_http_stat_create_srv_conf, /* create server configuration */ 187 | NULL, /* merge server configuration */ 188 | 189 | ngx_http_stat_create_loc_conf, /* create location configuration */ 190 | ngx_http_stat_merge_loc_conf, /* merge location configuration */ 191 | }; 192 | 193 | 194 | ngx_module_t ngx_http_stat_module = { 195 | NGX_MODULE_V1, 196 | &ngx_http_stat_module_ctx, /* module ctx */ 197 | ngx_http_stat_commands, /* module directives */ 198 | NGX_HTTP_MODULE, /* module type */ 199 | NULL, /* init master */ 200 | NULL, /* init module */ 201 | ngx_http_stat_process_init, /* init process */ 202 | NULL, /* init thread */ 203 | NULL, /* exit thread */ 204 | NULL, /* exit process */ 205 | NULL, /* exit master */ 206 | NGX_MODULE_V1_PADDING 207 | }; 208 | 209 | static ngx_str_t stat_shared_name = ngx_string("_ngx_stat_module"); 210 | 211 | static ngx_http_variable_t ngx_http_stat_vars[] = { 212 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 213 | }; 214 | 215 | static ngx_http_stat_arg_t ngx_http_stat_config_args[] = { 216 | { ngx_string("host"), 217 | ngx_http_stat_config_arg_host, 218 | ngx_null_string }, 219 | { ngx_string("server"), 220 | ngx_http_stat_config_arg_server, 221 | ngx_null_string }, 222 | { ngx_string("port"), 223 | ngx_http_stat_config_arg_port, 224 | ngx_string("8089") }, 225 | { ngx_string("frequency"), 226 | ngx_http_stat_config_arg_frequency, 227 | ngx_string("60") }, 228 | { ngx_string("intervals"), 229 | ngx_http_stat_config_arg_intervals, 230 | ngx_string("1m") }, 231 | { ngx_string("params"), 232 | ngx_http_stat_config_arg_params, 233 | ngx_string(DEFAULT_PARAMS)}, 234 | { ngx_string("shared"), 235 | ngx_http_stat_config_arg_shared, 236 | ngx_string("1m") }, 237 | { ngx_string("buffer"), 238 | ngx_http_stat_config_arg_buffer, 239 | ngx_string("64k") }, 240 | { ngx_string("package"), 241 | ngx_http_stat_config_arg_package, 242 | ngx_string("1400") }, 243 | { ngx_string("template"), 244 | ngx_http_stat_config_arg_template, 245 | ngx_null_string }, 246 | { ngx_string("protocol"), 247 | ngx_http_stat_config_arg_protocol, 248 | ngx_null_string }, 249 | { ngx_string("timeout"), 250 | ngx_http_stat_config_arg_timeout, 251 | ngx_string("100") } 252 | }; 253 | 254 | 255 | static ngx_http_stat_arg_t ngx_http_stat_param_args[] = { 256 | { ngx_string("name"), 257 | ngx_http_stat_param_arg_name, 258 | ngx_null_string }, 259 | { ngx_string("aggregate"), 260 | ngx_http_stat_param_arg_aggregate, 261 | ngx_null_string }, 262 | { ngx_string("interval"), 263 | ngx_http_stat_param_arg_interval, 264 | ngx_null_string }, 265 | { ngx_string("percentile"), 266 | ngx_http_stat_param_arg_percentile, 267 | ngx_null_string }, 268 | }; 269 | 270 | static ngx_event_t timer; 271 | 272 | /** Metrics & acc functions & statistics {{{ */ 273 | static ngx_http_stat_aggregate_t ngx_http_stat_aggregates[] = { 274 | 275 | { ngx_string("avg"), 276 | ngx_http_stat_aggregate_avg }, 277 | { ngx_string("persec"), 278 | ngx_http_stat_aggregate_persec }, 279 | { ngx_string("sum"), 280 | ngx_http_stat_aggregate_sum }, 281 | }; 282 | /** }}} */ 283 | 284 | static ngx_http_stat_source_t ngx_http_stat_sources[] = { 285 | 286 | { .name = ngx_string("request_time"), 287 | .get = ngx_http_stat_source_request_time, 288 | .aggregate = ngx_http_stat_aggregate_avg }, 289 | 290 | { .name = ngx_string("bytes_sent"), 291 | .get = ngx_http_stat_source_bytes_sent, 292 | .aggregate = ngx_http_stat_aggregate_avg }, 293 | 294 | { .name = ngx_string("body_bytes_sent"), 295 | .get = ngx_http_stat_source_body_bytes_sent, 296 | .aggregate = ngx_http_stat_aggregate_avg }, 297 | 298 | { .name = ngx_string("request_length"), 299 | .get = ngx_http_stat_source_request_length, 300 | .aggregate = ngx_http_stat_aggregate_avg }, 301 | 302 | { .name = ngx_string("rps"), 303 | .get = ngx_http_stat_source_rps, 304 | .aggregate = ngx_http_stat_aggregate_persec }, 305 | 306 | { .name = ngx_string("keepalive_rps"), 307 | .get = ngx_http_stat_source_keepalive_rps, 308 | .aggregate = ngx_http_stat_aggregate_persec }, 309 | 310 | { .name = ngx_string("response_2xx_rps"), 311 | .get = ngx_http_stat_source_response_2xx_rps, 312 | .aggregate = ngx_http_stat_aggregate_persec }, 313 | 314 | { .name = ngx_string("response_3xx_rps"), 315 | .get = ngx_http_stat_source_response_3xx_rps, 316 | .aggregate = ngx_http_stat_aggregate_persec }, 317 | 318 | { .name = ngx_string("response_4xx_rps"), 319 | .get = ngx_http_stat_source_response_4xx_rps, 320 | .aggregate = ngx_http_stat_aggregate_persec }, 321 | 322 | { .name = ngx_string("response_5xx_rps"), 323 | .get = ngx_http_stat_source_response_5xx_rps, 324 | .aggregate = ngx_http_stat_aggregate_persec }, 325 | 326 | { .name = ngx_string("response_\\d\\d\\d_rps"), 327 | .re = 1, 328 | .get = ngx_http_stat_source_response_xxx_rps, 329 | .aggregate = ngx_http_stat_aggregate_persec }, 330 | 331 | { .name = ngx_string("upstream_cache_(miss|bypass|expired|stale|updating|revalidated|hit)_rps"), 332 | .re = 1, 333 | .get = ngx_http_stat_source_upstream_cache_status_rps, 334 | .aggregate = ngx_http_stat_aggregate_persec }, 335 | #if 0 336 | { .name = ngx_string("upstream_time"), 337 | .get = ngx_http_stat_source_upstream_time, 338 | .aggregate = ngx_http_stat_aggregate_avg }, 339 | 340 | { .name = ngx_string("upstream_header_time"), 341 | .get = ngx_http_stat_source_upstream_header_time, 342 | .aggregate = ngx_http_stat_aggregate_avg }, 343 | #endif 344 | }; 345 | 346 | static ngx_http_stat_template_arg_t ngx_http_stat_template_args[] = { 347 | { ngx_string("host"), TEMPLATE_VARIABLE_HOST }, 348 | { ngx_string("split"), TEMPLATE_VARIABLE_SPLIT }, 349 | { ngx_string("param"), TEMPLATE_VARIABLE_PARAM }, 350 | { ngx_string("interval"), TEMPLATE_VARIABLE_INTERVAL }, 351 | }; 352 | 353 | static ngx_http_stat_template_arg_t ngx_http_stat_default_data_args[] = { 354 | { ngx_string("location"), TEMPLATE_VARIABLE_LOCATION }, 355 | }; 356 | /** }}} */ 357 | 358 | /** Implementation {{{ */ 359 | static 360 | ngx_int_t 361 | ngx_http_stat_add_variables(ngx_conf_t *cf) 362 | { 363 | ngx_http_variable_t *var, *v; 364 | 365 | for (v = ngx_http_stat_vars; v->name.len; v++) { 366 | var = ngx_http_add_variable(cf, &v->name, v->flags); 367 | if (var == NULL) { 368 | return NGX_ERROR; 369 | } 370 | 371 | var->get_handler = v->get_handler; 372 | var->data = v->data; 373 | } 374 | 375 | return NGX_OK; 376 | } 377 | 378 | 379 | static 380 | ngx_int_t 381 | ngx_http_stat_init(ngx_conf_t *cf) 382 | { 383 | ngx_http_handler_pt *h; 384 | ngx_http_core_main_conf_t *cmcf; 385 | 386 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 387 | 388 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); 389 | if (h == NULL) { 390 | return NGX_ERROR; 391 | } 392 | 393 | *h = ngx_http_stat_handler; 394 | 395 | return NGX_OK; 396 | } 397 | 398 | 399 | static 400 | ngx_int_t 401 | ngx_http_stat_process_init(ngx_cycle_t *cycle) 402 | { 403 | ngx_http_stat_main_conf_t *smcf; 404 | 405 | smcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_stat_module); 406 | 407 | if (!smcf->enable) { 408 | return NGX_OK; 409 | } 410 | 411 | ngx_memzero(&timer, sizeof(timer)); 412 | 413 | if (smcf->protocol.len > sizeof("influx/") - 1 && 414 | ngx_strncmp(smcf->protocol.data, "influx/udp", 415 | sizeof("influx/udp") - 1) == 0) 416 | { 417 | timer.handler = ngx_http_influx_udp_timer_handler; 418 | } else { 419 | ngx_log_error(NGX_LOG_CRIT, cycle->log, 0, 420 | "a protocol does not supported \"%V\", for server \"%V\"", 421 | &smcf->protocol, &smcf->server.name); 422 | return NGX_ERROR; 423 | } 424 | 425 | timer.data = smcf; 426 | timer.log = cycle->log; 427 | 428 | ngx_add_timer(&timer, smcf->frequency); 429 | 430 | return NGX_OK; 431 | } 432 | 433 | 434 | static 435 | void * 436 | ngx_http_stat_create_main_conf(ngx_conf_t *cf) 437 | { 438 | ngx_http_stat_main_conf_t *smcf; 439 | ngx_http_stat_allocator_t *allocator; 440 | 441 | smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_stat_main_conf_t)); 442 | if (smcf == NULL) { 443 | return NULL; 444 | } 445 | 446 | smcf->sources = ngx_array_create(cf->pool, 1, sizeof(ngx_http_stat_source_t)); 447 | smcf->splits = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); 448 | smcf->intervals = ngx_array_create(cf->pool, 1, sizeof(ngx_http_stat_interval_t)); 449 | smcf->default_params = ngx_array_create(cf->pool, 1, sizeof(ngx_uint_t)); 450 | smcf->template = ngx_array_create(cf->pool, 1, sizeof(ngx_http_stat_template_t)); 451 | smcf->default_data_template = ngx_array_create(cf->pool, 1, sizeof(ngx_http_stat_template_t)); 452 | smcf->default_data_params = ngx_array_create(cf->pool, 1, sizeof(ngx_uint_t)); 453 | smcf->datas = ngx_array_create(cf->pool, 1, sizeof(ngx_http_stat_data_t)); 454 | 455 | if (smcf->sources == NULL || 456 | smcf->splits == NULL || 457 | smcf->intervals == NULL || 458 | smcf->default_params == NULL || 459 | smcf->template == NULL || 460 | smcf->default_data_template == NULL || 461 | smcf->default_data_params == NULL || 462 | smcf->datas == NULL) 463 | { 464 | return NULL; 465 | } 466 | 467 | smcf->storage = ngx_pcalloc(cf->pool, sizeof(ngx_http_stat_storage_t)); 468 | if (smcf->storage == NULL) { 469 | return NULL; 470 | } 471 | 472 | allocator = ngx_palloc(cf->pool, sizeof(ngx_http_stat_allocator_t)); 473 | if (allocator == NULL) { 474 | return NULL; 475 | } 476 | 477 | ngx_http_stat_allocator_init(allocator, cf->pool, 478 | ngx_http_stat_allocator_pool_alloc, 479 | ngx_http_stat_allocator_pool_free); 480 | 481 | smcf->storage->allocator = allocator; 482 | smcf->storage->params = ngx_http_stat_array_create(allocator, 483 | 1, sizeof(ngx_http_stat_param_t)); 484 | smcf->storage->internals = ngx_http_stat_array_create(allocator, 485 | 1, sizeof(ngx_http_stat_internal_t)); 486 | smcf->storage->metrics = ngx_http_stat_array_create(allocator, 487 | 1, sizeof(ngx_http_stat_metric_t)); 488 | smcf->storage->statistics = ngx_http_stat_array_create(allocator, 489 | 1, sizeof(ngx_http_stat_statistic_t)); 490 | 491 | if (smcf->storage->params == NULL || 492 | smcf->storage->internals == NULL || 493 | smcf->storage->metrics == NULL || 494 | smcf->storage->statistics == NULL) 495 | { 496 | return NULL; 497 | } 498 | 499 | return smcf; 500 | } 501 | 502 | 503 | static 504 | void * 505 | ngx_http_stat_create_srv_conf(ngx_conf_t *cf) 506 | { 507 | ngx_http_stat_srv_conf_t *sscf; 508 | 509 | sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_stat_srv_conf_t)); 510 | if (sscf == NULL) { 511 | return NULL; 512 | } 513 | 514 | sscf->default_data_template = ngx_array_create(cf->pool, 515 | 1, sizeof(ngx_http_stat_template_t)); 516 | sscf->default_data_params = ngx_array_create(cf->pool, 517 | 1, sizeof(ngx_uint_t)); 518 | sscf->datas = ngx_array_create(cf->pool, 519 | 1, sizeof(ngx_http_stat_data_t)); 520 | 521 | if (sscf->default_data_template == NULL || 522 | sscf->default_data_params == NULL || 523 | sscf->datas == NULL) 524 | { 525 | return NULL; 526 | } 527 | 528 | return sscf; 529 | } 530 | 531 | 532 | static void * 533 | ngx_http_stat_create_loc_conf(ngx_conf_t *cf) 534 | { 535 | ngx_http_stat_main_conf_t *smcf; 536 | ngx_http_stat_loc_conf_t *slcf; 537 | ngx_http_stat_srv_conf_t *sscf; 538 | ngx_str_t loc, *uri, *location, *directive; 539 | 540 | slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_stat_loc_conf_t)); 541 | if (slcf == NULL) { 542 | return NULL; 543 | } 544 | 545 | slcf->datas = ngx_array_create(cf->pool, 1, 546 | sizeof(ngx_http_stat_data_t)); 547 | if (slcf->datas == NULL) { 548 | return NULL; 549 | } 550 | 551 | if (!cf->args) { 552 | return slcf; 553 | } 554 | 555 | directive = &((ngx_str_t*) cf->args->elts)[0]; 556 | 557 | ngx_str_set(&loc, "location"); 558 | 559 | smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_stat_module); 560 | 561 | if (cf->args->nelts >= 2 && 562 | directive->len == loc.len && 563 | ngx_strncmp(directive->data, loc.data, loc.len) == 0) 564 | { 565 | sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_stat_module); 566 | 567 | uri = &((ngx_str_t*)cf->args->elts)[cf->args->nelts - 1]; 568 | location = ngx_http_stat_location(cf->pool, uri); 569 | if (location == NULL) { 570 | return NULL; 571 | } 572 | 573 | if (location->len == 0) { 574 | return slcf; 575 | } 576 | 577 | if (ngx_http_stat_add_default_data(cf, slcf->datas, location, 578 | smcf->default_data_template, smcf->default_data_params, 579 | smcf->default_data_filter) != NGX_CONF_OK) 580 | { 581 | return NULL; 582 | } 583 | 584 | if (ngx_http_stat_add_default_data(cf, slcf->datas, location, 585 | sscf->default_data_template, sscf->default_data_params, 586 | sscf->default_data_filter) != NGX_CONF_OK) { 587 | return NULL; 588 | } 589 | } 590 | 591 | return slcf; 592 | } 593 | 594 | 595 | 596 | static 597 | char * 598 | ngx_http_stat_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) 599 | { 600 | ngx_http_stat_loc_conf_t *prev = parent; 601 | ngx_http_stat_loc_conf_t *conf = child; 602 | 603 | ngx_uint_t i; 604 | ngx_http_stat_data_t *prev_data; 605 | ngx_http_stat_data_t *data; 606 | 607 | for (i = 0; i < prev->datas->nelts; i++) { 608 | prev_data = &((ngx_http_stat_data_t*) prev->datas->elts)[i]; 609 | data = ngx_array_push(conf->datas); 610 | if (data == NULL) { 611 | return NGX_CONF_ERROR; 612 | } 613 | 614 | *data = *prev_data; 615 | } 616 | 617 | return NGX_CONF_OK; 618 | } 619 | 620 | 621 | static 622 | ngx_str_t * 623 | ngx_http_stat_location(ngx_pool_t *pool, ngx_str_t *uri) 624 | { 625 | ngx_str_t *split; 626 | ngx_uint_t i; 627 | 628 | split = ngx_palloc(pool, sizeof(ngx_str_t)); 629 | if (split == NULL) { 630 | return NULL; 631 | } 632 | 633 | split->data = ngx_palloc(pool, uri->len); 634 | split->len = 0; 635 | if (split->data == NULL) { 636 | return NULL; 637 | } 638 | 639 | for (i = 0; i < uri->len; i++) { 640 | 641 | if (isalnum(uri->data[i])) { 642 | split->data[split->len++] = uri->data[i]; 643 | } else { 644 | split->data[split->len++] = '_'; 645 | } 646 | } 647 | 648 | while (split->len > 0 && split->data[0] == '_') { 649 | split->data++; 650 | split->len--; 651 | } 652 | 653 | while (split->len > 0 && split->data[split->len - 1] == '_') { 654 | split->len--; 655 | } 656 | 657 | return split; 658 | } 659 | 660 | 661 | static 662 | ngx_http_stat_ctx_t 663 | ngx_http_stat_ctx_from_config(ngx_conf_t *cf) 664 | { 665 | ngx_http_stat_main_conf_t *smcf; 666 | ngx_http_stat_ctx_t ctx; 667 | 668 | smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_stat_module); 669 | 670 | ctx.phase = PHASE_CONFIG; 671 | ctx.storage = smcf->storage; 672 | ctx.pool = cf->pool; 673 | ctx.log = cf->log; 674 | ctx.smcf = smcf; 675 | 676 | return ctx; 677 | } 678 | 679 | 680 | static 681 | ngx_http_stat_ctx_t 682 | ngx_http_stat_ctx_from_request(ngx_http_request_t *r) 683 | { 684 | ngx_http_stat_main_conf_t *smcf; 685 | ngx_http_stat_storage_t *storage; 686 | ngx_slab_pool_t *shpool; 687 | 688 | storage = NULL; 689 | 690 | smcf = ngx_http_get_module_main_conf(r, ngx_http_stat_module); 691 | 692 | if (smcf->enable) { 693 | shpool = (ngx_slab_pool_t*)smcf->shared->shm.addr; 694 | storage = (ngx_http_stat_storage_t*)shpool->data; 695 | } 696 | 697 | ngx_http_stat_ctx_t ctx; 698 | ctx.phase = PHASE_REQUEST; 699 | ctx.storage = storage; 700 | ctx.pool = NULL; 701 | ctx.log = r->connection->log; 702 | ctx.smcf = smcf; 703 | 704 | return ctx; 705 | } 706 | 707 | 708 | static 709 | char * 710 | ngx_http_stat_parse_args(ngx_http_stat_ctx_t *ctx, 711 | ngx_array_t *vars, void *conf, ngx_http_stat_arg_t *args, 712 | ngx_uint_t args_count) 713 | { 714 | ngx_uint_t i, j, find; 715 | ngx_str_t *var, value; 716 | ngx_uint_t isset[args_count]; 717 | ngx_http_stat_arg_t *arg; 718 | 719 | ngx_memzero(isset, args_count * sizeof(ngx_uint_t)); 720 | 721 | for (i = 1; i < vars->nelts; i++) { 722 | 723 | find = 0; 724 | var = &((ngx_str_t*) vars->elts)[i]; 725 | 726 | for (j = 0; j < args_count; j++) { 727 | 728 | arg = &args[j]; 729 | 730 | if (!ngx_strncmp(arg->name.data, var->data, arg->name.len) && 731 | var->data[arg->name.len] == '=') 732 | { 733 | isset[j] = 1; 734 | find = 1; 735 | value.data = var->data + arg->name.len + 1; 736 | value.len = var->len - (arg->name.len + 1); 737 | 738 | if (arg->handler(ctx, conf, &value) == NGX_CONF_ERROR) { 739 | return NGX_CONF_ERROR; 740 | } 741 | 742 | break; 743 | } 744 | } 745 | 746 | if (!find) { 747 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 748 | "stat unknown option \"%V\"", var); 749 | return NGX_CONF_ERROR; 750 | } 751 | } 752 | 753 | for (i = 0; i < args_count; i++) { 754 | 755 | if (isset[i]) { 756 | continue; 757 | } 758 | 759 | arg = &args[i]; 760 | if (arg->deflt.len == 0) { 761 | continue; 762 | } 763 | 764 | if (arg->handler(ctx, conf, (ngx_str_t*)&arg->deflt) 765 | == NGX_CONF_ERROR) 766 | { 767 | ngx_log_error(NGX_LOG_CRIT, ctx->log, 0, 768 | "stat invalid option default value \"%V\"", &arg->deflt); 769 | return NGX_CONF_ERROR; 770 | } 771 | } 772 | 773 | return NGX_CONF_OK; 774 | } 775 | 776 | 777 | static 778 | ngx_int_t 779 | ngx_http_stat_init_data(ngx_http_stat_ctx_t *ctx, 780 | ngx_http_stat_data_t *data) 781 | { 782 | ngx_http_stat_allocator_t *allocator; 783 | 784 | allocator = ctx->storage->allocator; 785 | 786 | data->metrics = ngx_http_stat_array_create(allocator, 1, 787 | sizeof(ngx_uint_t)); 788 | data->statistics = ngx_http_stat_array_create(allocator, 1, 789 | sizeof(ngx_uint_t)); 790 | 791 | if (data->metrics == NULL || data->statistics == NULL) { 792 | return NGX_ERROR; 793 | } 794 | 795 | data->filter = NULL; 796 | 797 | return NGX_OK; 798 | } 799 | 800 | 801 | static 802 | ngx_int_t 803 | ngx_http_stat_add_param_to_config(ngx_http_stat_ctx_t *ctx, 804 | ngx_http_stat_param_t *param) 805 | { 806 | ngx_uint_t p; 807 | ngx_http_stat_array_t *params; 808 | ngx_http_stat_param_t *old_param, *new_param; 809 | 810 | params = ctx->storage->params; 811 | 812 | for (p = 0; p < params->nelts; p++) { 813 | 814 | old_param = &((ngx_http_stat_param_t*) params->elts)[p]; 815 | 816 | if (param->name.len == old_param->name.len && 817 | ngx_strncmp(param->name.data, old_param->name.data, 818 | param->name.len) == 0) 819 | { 820 | if (!param->percentile && !old_param->percentile) { 821 | 822 | if (param->aggregate == old_param->aggregate && 823 | param->interval.value == old_param->interval.value) 824 | { 825 | return p; 826 | } else { 827 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 828 | "stat param with different aggregate or interval"); 829 | return NGX_ERROR; 830 | } 831 | } 832 | else if (param->percentile == old_param->percentile) { 833 | return p; 834 | } 835 | } 836 | } 837 | 838 | new_param = ngx_http_stat_array_push(params); 839 | if (!new_param) { 840 | return NGX_ERROR; 841 | } 842 | 843 | *new_param = *param; 844 | 845 | return params->nelts - 1; 846 | } 847 | 848 | 849 | static 850 | char * 851 | ngx_http_stat_add_param_to_data(ngx_http_stat_ctx_t *ctx, 852 | ngx_uint_t split, ngx_uint_t param, ngx_http_stat_data_t *data) 853 | { 854 | ngx_uint_t i, *m, *s; 855 | ngx_http_stat_storage_t *storage; 856 | ngx_http_stat_param_t *p; 857 | ngx_http_stat_metric_t *metric; 858 | ngx_http_stat_statistic_t *statistic; 859 | 860 | storage = ctx->storage; 861 | 862 | p = &((ngx_http_stat_param_t*) storage->params->elts)[param]; 863 | 864 | if (p->percentile == 0) { 865 | 866 | for (i = 0; i < storage->metrics->nelts; i++) { 867 | 868 | metric = &((ngx_http_stat_metric_t*) storage->metrics->elts)[i]; 869 | 870 | if (metric->split == split && metric->param == param) { 871 | break; 872 | } 873 | } 874 | 875 | if (i == storage->metrics->nelts) { 876 | 877 | metric = ngx_http_stat_array_push(storage->metrics); 878 | if (metric == NULL) { 879 | return NGX_CONF_ERROR; 880 | } 881 | 882 | metric->split = split; 883 | metric->param = param; 884 | metric->acc = NULL; 885 | 886 | if (ctx->phase == PHASE_REQUEST) { 887 | metric->acc = ngx_http_stat_allocator_alloc( 888 | storage->allocator, 889 | sizeof(ngx_http_stat_acc_t) * (storage->max_interval + 1)); 890 | if (metric->acc == NULL) { 891 | return NGX_CONF_ERROR; 892 | } 893 | 894 | ngx_memzero(metric->acc, 895 | sizeof(ngx_http_stat_acc_t) * (storage->max_interval + 1)); 896 | } 897 | } 898 | 899 | m = ngx_http_stat_array_push(data->metrics); 900 | if (m == NULL) { 901 | return NGX_CONF_ERROR; 902 | } 903 | 904 | *m = i; 905 | 906 | } else { 907 | for (i = 0; i < storage->statistics->nelts; i++) { 908 | 909 | statistic = 910 | &((ngx_http_stat_statistic_t*) storage->statistics->elts)[i]; 911 | 912 | if (statistic->split == split && statistic->param == param) { 913 | break; 914 | } 915 | } 916 | 917 | if (i == storage->statistics->nelts) { 918 | 919 | statistic = ngx_http_stat_array_push(storage->statistics); 920 | if (statistic == NULL) { 921 | return NGX_CONF_ERROR; 922 | } 923 | 924 | statistic->split = split; 925 | statistic->param = param; 926 | statistic->stt = NULL; 927 | 928 | if (ctx->phase == PHASE_REQUEST) { 929 | 930 | statistic->stt = ngx_http_stat_allocator_alloc( 931 | storage->allocator, sizeof(ngx_http_stat_stt_t)); 932 | if (statistic->stt == NULL) { 933 | return NGX_CONF_ERROR; 934 | } 935 | 936 | ngx_memzero(statistic->stt, sizeof(ngx_http_stat_stt_t)); 937 | } 938 | } 939 | 940 | s = ngx_http_stat_array_push(data->statistics); 941 | if (s == NULL) { 942 | return NGX_CONF_ERROR; 943 | } 944 | 945 | *s = i; 946 | } 947 | 948 | return NGX_CONF_OK; 949 | } 950 | 951 | 952 | static 953 | char * 954 | ngx_http_stat_add_param_to_params(ngx_http_stat_ctx_t *ctx, 955 | ngx_uint_t param, ngx_array_t *params) 956 | { 957 | ngx_http_stat_main_conf_t *smcf; 958 | ngx_uint_t i, p, *new_param; 959 | ngx_http_stat_param_t *old_param; 960 | 961 | smcf = ctx->smcf; 962 | 963 | for (i = 0; i < params->nelts; i++) { 964 | 965 | p = ((ngx_uint_t*) params->elts)[i]; 966 | 967 | if (p != param) { 968 | continue; 969 | } 970 | 971 | old_param = &((ngx_http_stat_param_t*) 972 | smcf->storage->params->elts)[p]; 973 | 974 | if (old_param->percentile == 0) { 975 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 976 | "stat duplicate param %V", &old_param->name); 977 | } else { 978 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 979 | "stat duplicate param %V/%ui", 980 | &old_param->name, old_param->percentile); 981 | return NGX_CONF_ERROR; 982 | } 983 | } 984 | 985 | new_param = ngx_array_push(params); 986 | if (new_param == NULL) { 987 | return NGX_CONF_ERROR; 988 | } 989 | 990 | *new_param = param; 991 | 992 | return NGX_CONF_OK; 993 | } 994 | 995 | 996 | static 997 | char * 998 | ngx_http_stat_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 999 | { 1000 | char *rc, host[HOST_LEN], *dot; 1001 | ngx_uint_t host_size; 1002 | ngx_http_stat_ctx_t ctx; 1003 | ngx_url_t u; 1004 | ngx_str_t stat_shared_id; 1005 | ngx_http_stat_main_conf_t *smcf; 1006 | 1007 | ctx = ngx_http_stat_ctx_from_config(cf); 1008 | 1009 | rc = ngx_http_stat_parse_args(&ctx, cf->args, conf, 1010 | ngx_http_stat_config_args, 1011 | ARR_SIZE(ngx_http_stat_config_args)); 1012 | 1013 | if (rc == NGX_CONF_ERROR) { 1014 | return NGX_CONF_ERROR; 1015 | } 1016 | 1017 | smcf = conf; 1018 | 1019 | if (smcf->host.len == 0) { 1020 | 1021 | gethostname(host, HOST_LEN); 1022 | host[HOST_LEN - 1] = '\0'; 1023 | dot = strchr(host, '.'); 1024 | if (dot) { 1025 | *dot = '\0'; 1026 | } 1027 | 1028 | host_size = strlen(host); 1029 | 1030 | smcf->host.data = ngx_palloc(cf->pool, host_size); 1031 | if (smcf->host.data == NULL) { 1032 | return NGX_CONF_ERROR; 1033 | } 1034 | 1035 | ngx_memcpy(smcf->host.data, host, host_size); 1036 | smcf->host.len = host_size; 1037 | } 1038 | 1039 | if (smcf->server.name.len == 0) { 1040 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1041 | "stat config server not set"); 1042 | return NGX_CONF_ERROR; 1043 | } 1044 | 1045 | if (smcf->port < 1 || smcf->port > 65535) { 1046 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1047 | "stat config port must be in range form 1 to 65535"); 1048 | return NGX_CONF_ERROR; 1049 | } 1050 | 1051 | if (smcf->frequency < 1 || smcf->frequency > 65535) { 1052 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1053 | "stat config frequency must be in range form 1 to 65535"); 1054 | return NGX_CONF_ERROR; 1055 | } 1056 | 1057 | if (smcf->intervals->nelts == 0) { 1058 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1059 | "stat config intervals not set"); 1060 | return NGX_CONF_ERROR; 1061 | } 1062 | 1063 | if (smcf->default_params->nelts == 0) { 1064 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1065 | "stat config params not set"); 1066 | return NGX_CONF_ERROR; 1067 | } 1068 | 1069 | if (smcf->shared_size == 0 || smcf->buffer_size == 0) { 1070 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1071 | "stat config shared must be positive value"); 1072 | return NGX_CONF_ERROR; 1073 | } 1074 | 1075 | if (smcf->buffer_size == 0) { 1076 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1077 | "stat config buffer must be positive value"); 1078 | return NGX_CONF_ERROR; 1079 | } 1080 | 1081 | if (smcf->package_size == 0) { 1082 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1083 | "stat config package must be positive value"); 1084 | return NGX_CONF_ERROR; 1085 | } 1086 | 1087 | if (smcf->shared_size < sizeof(ngx_slab_pool_t)) { 1088 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1089 | "stat too small shared memory"); 1090 | return NGX_CONF_ERROR; 1091 | } 1092 | 1093 | ngx_memzero(&u, sizeof(ngx_url_t)); 1094 | 1095 | u.url = smcf->server.name; 1096 | u.default_port = smcf->port; 1097 | 1098 | if (ngx_parse_url(cf->pool, &u) != NGX_OK) { 1099 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1100 | "\"%s\" in resolver \"%V\"", u.err, &u.url); 1101 | return NGX_CONF_ERROR; 1102 | } 1103 | 1104 | smcf->server.sockaddr = u.addrs[0].sockaddr; 1105 | smcf->server.socklen = u.addrs[0].socklen; 1106 | 1107 | stat_shared_id.len = stat_shared_name.len + 32; 1108 | stat_shared_id.data = ngx_palloc(cf->pool, stat_shared_id.len); 1109 | if (stat_shared_id.data == NULL) { 1110 | return NGX_CONF_ERROR; 1111 | } 1112 | 1113 | ngx_snprintf(stat_shared_id.data, stat_shared_id.len, 1114 | "%V.%T", &stat_shared_name, ngx_time()); 1115 | 1116 | smcf->shared = ngx_shared_memory_add(cf, &stat_shared_id, 1117 | smcf->shared_size, &ngx_http_stat_module); 1118 | if (smcf->shared == NULL) { 1119 | return NGX_CONF_ERROR; 1120 | } 1121 | if (smcf->shared->data) { 1122 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1123 | "stat shared memory is used"); 1124 | return NGX_CONF_ERROR; 1125 | } 1126 | smcf->shared->init = ngx_http_stat_shared_init; 1127 | smcf->shared->data = smcf; 1128 | 1129 | smcf->buffer.start = ngx_palloc(cf->pool, smcf->buffer_size); 1130 | if (smcf->buffer.start == NULL) { 1131 | return NGX_CONF_ERROR; 1132 | } 1133 | smcf->buffer.end = smcf->buffer.start + smcf->buffer_size; 1134 | 1135 | smcf->enable = 1; 1136 | 1137 | return NGX_CONF_OK; 1138 | } 1139 | 1140 | 1141 | static 1142 | char * 1143 | ngx_http_stat_default_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1144 | { 1145 | ngx_http_stat_ctx_t ctx; 1146 | ngx_http_stat_main_conf_t *smcf; 1147 | ngx_http_stat_srv_conf_t *sscf; 1148 | ngx_str_t *args, v, value; 1149 | ngx_array_t *template, *params; 1150 | ngx_http_complex_value_t **filter; 1151 | ngx_uint_t i; 1152 | 1153 | ctx = ngx_http_stat_ctx_from_config(cf); 1154 | 1155 | smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_stat_module); 1156 | sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_stat_module); 1157 | 1158 | if (!smcf->enable) { 1159 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "stat config not set"); 1160 | return NGX_CONF_ERROR; 1161 | } 1162 | 1163 | args = cf->args->elts; 1164 | 1165 | template = NULL; 1166 | params = NULL; 1167 | 1168 | if (cf->cmd_type == NGX_HTTP_MAIN_CONF) { 1169 | template = smcf->default_data_template; 1170 | params = smcf->default_data_params; 1171 | filter = &smcf->default_data_filter; 1172 | } 1173 | else if (cf->cmd_type == NGX_HTTP_SRV_CONF) { 1174 | template = sscf->default_data_template; 1175 | params = sscf->default_data_params; 1176 | filter = &sscf->default_data_filter; 1177 | } 1178 | else { 1179 | return NGX_CONF_ERROR; 1180 | } 1181 | 1182 | if (ngx_http_stat_template_compile(&ctx, template, 1183 | ngx_http_stat_default_data_args, 1184 | ARR_SIZE(ngx_http_stat_default_data_args), 1185 | &args[1]) != NGX_CONF_OK) 1186 | { 1187 | return NGX_CONF_ERROR; 1188 | } 1189 | 1190 | if (cf->args->nelts >= 3) { 1191 | 1192 | for (i = 2; i < cf->args->nelts; i++) { 1193 | 1194 | v = args[i]; 1195 | 1196 | if (v.len >= sizeof("params=") - 1 && 1197 | ngx_strncmp(v.data, "params=", sizeof("params") - 1) == 0) 1198 | { 1199 | value.len = args[i].len - 7; 1200 | value.data = args[i].data + 7; 1201 | 1202 | if (ngx_http_stat_parse_params(&ctx, &value, params) 1203 | != NGX_CONF_OK) 1204 | { 1205 | return NGX_CONF_ERROR; 1206 | } 1207 | 1208 | } else if (v.len >= sizeof("if") - 1 && 1209 | ngx_strncmp(args[i].data, "if=", sizeof("if") - 1) == 0) 1210 | { 1211 | value.len = args[i].len - 3; 1212 | value.data = args[i].data + 3; 1213 | 1214 | *filter = ngx_http_stat_complex_compile(cf, &value); 1215 | if (!*filter) { 1216 | return NGX_CONF_ERROR; 1217 | } 1218 | 1219 | } else { 1220 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1221 | "stat unknown option \"%V\"", &v); 1222 | return NGX_CONF_ERROR; 1223 | } 1224 | } 1225 | } 1226 | 1227 | return NGX_CONF_OK; 1228 | } 1229 | 1230 | 1231 | static 1232 | char * 1233 | ngx_http_stat_data(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1234 | { 1235 | ngx_http_stat_ctx_t ctx; 1236 | ngx_http_stat_main_conf_t *smcf; 1237 | ngx_http_stat_srv_conf_t *sscf; 1238 | ngx_http_stat_loc_conf_t *slcf; 1239 | ngx_str_t *args, *split, v, value; 1240 | ngx_array_t *params, *datas; 1241 | ngx_http_complex_value_t *filter; 1242 | ngx_uint_t i; 1243 | 1244 | ctx = ngx_http_stat_ctx_from_config(cf); 1245 | 1246 | smcf = ctx.smcf; 1247 | sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_stat_module); 1248 | slcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_stat_module); 1249 | 1250 | if (!smcf->enable) { 1251 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "stat config not set"); 1252 | return NGX_CONF_ERROR; 1253 | } 1254 | 1255 | args = cf->args->elts; 1256 | 1257 | split = &args[1]; 1258 | 1259 | params = ngx_array_create(cf->pool, 1, sizeof(ngx_uint_t)); 1260 | if (params == NULL) { 1261 | return NULL; 1262 | } 1263 | 1264 | filter = NULL; 1265 | 1266 | if (cf->args->nelts >= 3) { 1267 | 1268 | for (i = 2; i < cf->args->nelts; i++) { 1269 | 1270 | v = args[i]; 1271 | 1272 | if (v.len >= sizeof("params") - 1 && 1273 | ngx_strncmp(v.data, "params=", sizeof("params=") - 1) == 0) 1274 | { 1275 | value.len = args[i].len - 7; 1276 | value.data = args[i].data + 7; 1277 | 1278 | if (ngx_http_stat_parse_params(&ctx, &value, params) 1279 | != NGX_CONF_OK) 1280 | { 1281 | return NGX_CONF_ERROR; 1282 | } 1283 | } 1284 | else if (v.len >= sizeof("if") - 1 && 1285 | ngx_strncmp(args[i].data, "if=", sizeof("if=") - 1) == 0) 1286 | { 1287 | value.len = args[i].len - 3; 1288 | value.data = args[i].data + 3; 1289 | 1290 | filter = ngx_http_stat_complex_compile(cf, &value); 1291 | if (filter == NULL) { 1292 | return NGX_CONF_ERROR; 1293 | } 1294 | } 1295 | else { 1296 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 1297 | "stat unknown option \"%V\"", &v); 1298 | return NGX_CONF_ERROR; 1299 | } 1300 | } 1301 | } 1302 | 1303 | datas = slcf->datas; 1304 | 1305 | if (cf->cmd_type & NGX_HTTP_MAIN_CONF) { 1306 | datas = smcf->datas; 1307 | } else if (cf->cmd_type & NGX_HTTP_SRV_CONF) { 1308 | datas = sscf->datas; 1309 | } 1310 | 1311 | if (ngx_http_stat_add_data(cf, datas, split, params, filter) 1312 | != NGX_CONF_OK) 1313 | { 1314 | return NGX_CONF_ERROR; 1315 | } 1316 | 1317 | return NGX_CONF_OK; 1318 | } 1319 | 1320 | 1321 | static 1322 | ngx_int_t 1323 | ngx_http_stat_search_param(ngx_http_stat_array_t *internals, 1324 | ngx_str_t *name, ngx_int_t *found) 1325 | { 1326 | ngx_int_t rc; 1327 | ngx_uint_t i, min_len; 1328 | ngx_http_stat_internal_t *internal; 1329 | 1330 | *found = 0; 1331 | 1332 | for (i = 0; i < internals->nelts; i++) { 1333 | 1334 | internal = &((ngx_http_stat_internal_t*) internals->elts)[i]; 1335 | 1336 | min_len = internal->name.len < name->len ? 1337 | internal->name.len : name->len; 1338 | 1339 | rc = ngx_strncmp(internal->name.data, name->data, min_len); 1340 | 1341 | if (rc == 0 && internal->name.len == name->len) { 1342 | *found = 1; 1343 | break; 1344 | } 1345 | 1346 | if (rc > 0) { 1347 | break; 1348 | } 1349 | } 1350 | 1351 | return i; 1352 | } 1353 | 1354 | 1355 | static 1356 | ngx_array_t * 1357 | ngx_http_stat_create_param_args(ngx_pool_t *pool, ngx_str_t *name, 1358 | char *config) 1359 | { 1360 | ngx_array_t *args; 1361 | ngx_str_t *n, *arg; 1362 | char *p, *end, *begin; 1363 | 1364 | args = ngx_array_create(pool, 4, sizeof(ngx_str_t)); 1365 | if (args == NULL) { 1366 | return NULL; 1367 | } 1368 | 1369 | ngx_array_push(args); 1370 | 1371 | n = ngx_array_push(args); 1372 | if (n == NULL) { 1373 | ngx_array_destroy(args); 1374 | return NULL; 1375 | } 1376 | 1377 | n->data = ngx_palloc(pool, sizeof("name=") - 1 + name->len); 1378 | if (n->data == NULL) { 1379 | ngx_array_destroy(args); 1380 | return NULL; 1381 | } 1382 | 1383 | n->len = ngx_sprintf(n->data, "name=%V", name) - n->data; 1384 | 1385 | p = config; 1386 | 1387 | while (*p) { 1388 | 1389 | while (isspace(*p)) { 1390 | p++; 1391 | } 1392 | 1393 | begin = p; 1394 | 1395 | while (*p && !isspace(*p)) { 1396 | p++; 1397 | } 1398 | 1399 | end = p; 1400 | 1401 | if (end - begin != 0) { 1402 | 1403 | arg = ngx_array_push(args); 1404 | if (arg == NULL) { 1405 | ngx_array_destroy(args); 1406 | return NULL; 1407 | } 1408 | 1409 | arg->data = (u_char*)begin; 1410 | arg->len = end - begin; 1411 | } 1412 | else { 1413 | break; 1414 | } 1415 | } 1416 | 1417 | return args; 1418 | } 1419 | 1420 | 1421 | static 1422 | ngx_int_t 1423 | ngx_http_stat_parse_param_args(ngx_http_stat_ctx_t *ctx, 1424 | ngx_array_t *args, ngx_http_stat_param_t *param) 1425 | { 1426 | ngx_http_stat_allocator_t *allocator; 1427 | 1428 | allocator = ctx->storage->allocator; 1429 | 1430 | ngx_memzero(param, sizeof(ngx_http_stat_param_t)); 1431 | 1432 | param->percentiles = ngx_http_stat_array_create(allocator, 1, 1433 | sizeof(ngx_uint_t)); 1434 | if (param->percentiles == NULL) { 1435 | return NGX_ERROR; 1436 | } 1437 | 1438 | if (ngx_http_stat_parse_args(ctx, args, param, 1439 | ngx_http_stat_param_args, 1440 | ARR_SIZE(ngx_http_stat_param_args)) == NGX_CONF_ERROR) 1441 | { 1442 | return NGX_ERROR; 1443 | } 1444 | 1445 | param->source = SOURCE_INTERNAL; 1446 | 1447 | if (param->name.data == NULL) { 1448 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1449 | "stat param name not set"); 1450 | goto error_exit; 1451 | } 1452 | 1453 | if ((param->aggregate && !param->interval.value) || 1454 | (!param->aggregate && param->interval.value)) 1455 | { 1456 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1457 | "stat param must contain aggregate with interval"); 1458 | goto error_exit; 1459 | } 1460 | 1461 | if (!param->aggregate && !param->interval.value 1462 | && param->percentiles->nelts == 0) 1463 | { 1464 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1465 | "stat param must contain aggregate and interval or percentile"); 1466 | goto error_exit; 1467 | } 1468 | 1469 | if (param->interval.value > ctx->storage->max_interval) { 1470 | goto error_exit; 1471 | } 1472 | 1473 | return NGX_OK; 1474 | 1475 | error_exit: 1476 | ngx_http_stat_allocator_free(allocator, param->name.data); 1477 | ngx_http_stat_allocator_free(allocator, param->interval.name.data); 1478 | ngx_http_stat_array_destroy(param->percentiles); 1479 | 1480 | return NGX_ERROR; 1481 | } 1482 | 1483 | 1484 | static 1485 | ngx_int_t 1486 | ngx_http_stat_add_internal(ngx_http_stat_ctx_t *ctx, 1487 | ngx_http_stat_param_t *param) 1488 | { 1489 | 1490 | ngx_http_stat_storage_t *storage; 1491 | ngx_int_t found; 1492 | ngx_uint_t i, n, p; 1493 | ngx_http_stat_data_t data; 1494 | ngx_http_stat_param_t new_param; 1495 | ngx_http_stat_internal_t *internal; 1496 | 1497 | storage = ctx->storage; 1498 | 1499 | i = ngx_http_stat_search_param(storage->internals, ¶m->name, &found); 1500 | 1501 | if (!found) { 1502 | if (ngx_http_stat_init_data(ctx, &data) == NGX_ERROR) { 1503 | return NGX_ERROR; 1504 | } 1505 | } else { 1506 | data = ((ngx_http_stat_internal_t*) 1507 | storage->internals->elts)[i].data; 1508 | } 1509 | 1510 | new_param = *param; 1511 | 1512 | for (n = 0; n < param->percentiles->nelts + 1; n++) { 1513 | 1514 | if (n == 0) { 1515 | if (!new_param.interval.value || !new_param.aggregate) { 1516 | continue; 1517 | } 1518 | } else { 1519 | new_param.percentile = ((ngx_uint_t*) 1520 | new_param.percentiles->elts)[n - 1]; 1521 | } 1522 | 1523 | p = ngx_http_stat_add_param_to_config(ctx, &new_param); 1524 | if ((ngx_int_t) p == NGX_ERROR) { 1525 | return NGX_ERROR; 1526 | } 1527 | 1528 | if (ngx_http_stat_add_param_to_data(ctx, SPLIT_INTERNAL, p, 1529 | &data) != NGX_CONF_OK) 1530 | { 1531 | return NGX_ERROR; 1532 | } 1533 | } 1534 | 1535 | if (!found) { 1536 | 1537 | internal = ngx_http_stat_array_push(storage->internals); 1538 | if (internal == NULL) { 1539 | return NGX_ERROR; 1540 | } 1541 | 1542 | ngx_memmove( 1543 | &((ngx_http_stat_internal_t*) storage->internals->elts)[i + 1], 1544 | &((ngx_http_stat_internal_t*)storage->internals->elts)[i], 1545 | (storage->internals->nelts - i - 1) * sizeof(ngx_http_stat_internal_t)); 1546 | 1547 | internal = &((ngx_http_stat_internal_t*)storage->internals->elts)[i]; 1548 | internal->name = param->name; 1549 | internal->data = data; 1550 | } 1551 | 1552 | return i; 1553 | } 1554 | 1555 | 1556 | static 1557 | char * 1558 | ngx_http_stat_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1559 | { 1560 | ngx_http_stat_ctx_t ctx; 1561 | ngx_http_stat_main_conf_t *smcf; 1562 | ngx_http_stat_param_t param; 1563 | 1564 | ctx = ngx_http_stat_ctx_from_config(cf); 1565 | smcf = ctx.smcf; 1566 | 1567 | if (!smcf->enable) { 1568 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "stat config not set"); 1569 | return NGX_CONF_ERROR; 1570 | } 1571 | 1572 | if (ngx_http_stat_parse_param_args(&ctx, cf->args, ¶m) != NGX_OK) { 1573 | return NGX_CONF_ERROR; 1574 | } 1575 | 1576 | if (ngx_http_stat_add_internal(&ctx, ¶m) == NGX_ERROR) { 1577 | return NGX_CONF_ERROR; 1578 | } 1579 | 1580 | return NGX_CONF_OK; 1581 | } 1582 | 1583 | 1584 | static 1585 | char * 1586 | ngx_http_stat_config_arg_host(ngx_http_stat_ctx_t *ctx, 1587 | void *data, ngx_str_t *value) 1588 | { 1589 | ngx_http_stat_main_conf_t *smcf = data; 1590 | return ngx_http_stat_parse_string(ctx, value, &smcf->host); 1591 | } 1592 | 1593 | static 1594 | char * 1595 | ngx_http_stat_config_arg_protocol(ngx_http_stat_ctx_t *ctx, 1596 | void *data, ngx_str_t *value) 1597 | { 1598 | ngx_http_stat_main_conf_t *smcf = data; 1599 | return ngx_http_stat_parse_string(ctx, value, &smcf->protocol); 1600 | } 1601 | 1602 | 1603 | static 1604 | char * 1605 | ngx_http_stat_config_arg_timeout(ngx_http_stat_ctx_t *ctx, 1606 | void *data, ngx_str_t *value) 1607 | { 1608 | ngx_http_stat_main_conf_t *smcf = data; 1609 | smcf->timeout = ngx_atoi(value->data, value->len) * 1000; 1610 | return NGX_CONF_OK; 1611 | } 1612 | 1613 | 1614 | static 1615 | char * 1616 | ngx_http_stat_config_arg_server(ngx_http_stat_ctx_t *ctx, 1617 | void *data, ngx_str_t *value) 1618 | { 1619 | ngx_http_stat_main_conf_t *smcf = data; 1620 | return ngx_http_stat_parse_string(ctx, value, &smcf->server.name); 1621 | } 1622 | 1623 | 1624 | static 1625 | char * 1626 | ngx_http_stat_config_arg_port(ngx_http_stat_ctx_t *ctx, 1627 | void *data, ngx_str_t *value) 1628 | { 1629 | ngx_http_stat_main_conf_t *smcf = data; 1630 | smcf->port = ngx_atoi(value->data, value->len); 1631 | 1632 | return NGX_CONF_OK; 1633 | } 1634 | 1635 | 1636 | static 1637 | char * 1638 | ngx_http_stat_config_arg_frequency(ngx_http_stat_ctx_t *ctx, 1639 | void *data, ngx_str_t *value) 1640 | { 1641 | ngx_http_stat_main_conf_t *smcf = data; 1642 | smcf->frequency = ngx_atoi(value->data, value->len) * 1000; 1643 | 1644 | return NGX_CONF_OK; 1645 | } 1646 | 1647 | 1648 | static 1649 | char * 1650 | ngx_http_stat_config_arg_intervals(ngx_http_stat_ctx_t *ctx, 1651 | void *data, ngx_str_t *value) 1652 | { 1653 | ngx_http_stat_main_conf_t *smcf; 1654 | ngx_http_stat_allocator_t *allocator; 1655 | ngx_uint_t i, s; 1656 | ngx_http_stat_interval_t *interval; 1657 | 1658 | smcf = data; 1659 | allocator = ctx->storage->allocator; 1660 | s = 0; 1661 | 1662 | for (i = 0; i <= value->len; i++) { 1663 | 1664 | if (i == value->len || value->data[i] == '|') { 1665 | 1666 | if (i == s) { 1667 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1668 | "stat config intervals is empty"); 1669 | return NGX_CONF_ERROR; 1670 | } 1671 | 1672 | interval = ngx_array_push(smcf->intervals); 1673 | if (interval == NULL) { 1674 | return NGX_CONF_ERROR; 1675 | } 1676 | 1677 | interval->name.data = ngx_http_stat_allocator_alloc(allocator, i - s); 1678 | if (interval->name.data == NULL) { 1679 | return NGX_CONF_ERROR; 1680 | } 1681 | 1682 | ngx_memcpy(interval->name.data, &value->data[s], i - s); 1683 | interval->name.len = i - s; 1684 | 1685 | if (ngx_http_stat_parse_time(ctx, &interval->name, 1686 | &interval->value) == NGX_CONF_ERROR) 1687 | { 1688 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1689 | "stat config interval is invalid"); 1690 | return NGX_CONF_ERROR; 1691 | } 1692 | 1693 | if (interval->value > smcf->storage->max_interval) { 1694 | smcf->storage->max_interval = interval->value; 1695 | } 1696 | 1697 | s = i + 1; 1698 | } 1699 | } 1700 | 1701 | return NGX_CONF_OK; 1702 | } 1703 | 1704 | 1705 | static 1706 | char * 1707 | ngx_http_stat_config_arg_params(ngx_http_stat_ctx_t *ctx, void *conf, 1708 | ngx_str_t *value) 1709 | { 1710 | ngx_http_stat_main_conf_t *smcf = conf; 1711 | return ngx_http_stat_parse_params(ctx, value, smcf->default_params); 1712 | } 1713 | 1714 | 1715 | static 1716 | char * 1717 | ngx_http_stat_config_arg_shared(ngx_http_stat_ctx_t *ctx, void *conf, 1718 | ngx_str_t *value) 1719 | { 1720 | ngx_http_stat_main_conf_t *smcf = conf; 1721 | return ngx_http_stat_parse_size(ctx, value, &smcf->shared_size); 1722 | } 1723 | 1724 | static 1725 | char * 1726 | ngx_http_stat_config_arg_buffer(ngx_http_stat_ctx_t *ctx, void *conf, 1727 | ngx_str_t *value) 1728 | { 1729 | ngx_http_stat_main_conf_t *smcf = conf; 1730 | return ngx_http_stat_parse_size(ctx, value, &smcf->buffer_size); 1731 | } 1732 | 1733 | static 1734 | char * 1735 | ngx_http_stat_config_arg_package(ngx_http_stat_ctx_t *ctx, void *conf, 1736 | ngx_str_t *value) 1737 | { 1738 | ngx_http_stat_main_conf_t *smcf = conf; 1739 | return ngx_http_stat_parse_size(ctx, value, &smcf->package_size); 1740 | } 1741 | 1742 | static 1743 | char * 1744 | ngx_http_stat_config_arg_template(ngx_http_stat_ctx_t *ctx, void *conf, 1745 | ngx_str_t *value) 1746 | { 1747 | ngx_http_stat_main_conf_t *smcf = conf; 1748 | 1749 | return ngx_http_stat_template_compile(ctx, smcf->template, 1750 | ngx_http_stat_template_args, 1751 | ARR_SIZE(ngx_http_stat_template_args), 1752 | value); 1753 | } 1754 | 1755 | 1756 | static 1757 | char * 1758 | ngx_http_stat_param_arg_name(ngx_http_stat_ctx_t *ctx, void *data, 1759 | ngx_str_t *value) 1760 | { 1761 | ngx_http_stat_allocator_t *allocator; 1762 | ngx_http_stat_param_t *param; 1763 | 1764 | param = (ngx_http_stat_param_t *) data; 1765 | allocator = ctx->storage->allocator; 1766 | 1767 | param->name.data = ngx_http_stat_allocator_alloc(allocator, value->len); 1768 | if (param->name.data == NULL) { 1769 | return NGX_CONF_ERROR; 1770 | } 1771 | 1772 | ngx_memcpy(param->name.data, value->data, value->len); 1773 | param->name.len = value->len; 1774 | 1775 | return NGX_CONF_OK; 1776 | } 1777 | 1778 | static 1779 | char * 1780 | ngx_http_stat_param_arg_aggregate(ngx_http_stat_ctx_t *ctx, void *data, 1781 | ngx_str_t *value) 1782 | { 1783 | ngx_uint_t found, a; 1784 | ngx_http_stat_param_t *param; 1785 | 1786 | param = (ngx_http_stat_param_t *) data; 1787 | found = 0; 1788 | 1789 | for (a = 0; a < ARR_SIZE(ngx_http_stat_aggregates); a++) { 1790 | 1791 | if (ngx_http_stat_aggregates[a].name.len == value->len && 1792 | ngx_strncmp(ngx_http_stat_aggregates[a].name.data, 1793 | value->data, value->len) == 0) 1794 | { 1795 | found = 1; 1796 | param->aggregate = ngx_http_stat_aggregates[a].get; 1797 | break; 1798 | } 1799 | } 1800 | 1801 | if (!found) { 1802 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1803 | "stat param unknow aggregate \"%V\"", value); 1804 | return NGX_CONF_ERROR; 1805 | } 1806 | 1807 | return NGX_CONF_OK; 1808 | } 1809 | 1810 | 1811 | static 1812 | char * 1813 | ngx_http_stat_param_arg_interval(ngx_http_stat_ctx_t *ctx, void *data, 1814 | ngx_str_t *value) 1815 | { 1816 | ngx_http_stat_allocator_t *allocator; 1817 | ngx_http_stat_param_t *param; 1818 | ngx_http_stat_interval_t *interval; 1819 | 1820 | allocator = ctx->storage->allocator; 1821 | param = (ngx_http_stat_param_t*)data; 1822 | interval = ¶m->interval; 1823 | 1824 | interval->name.data = ngx_http_stat_allocator_alloc(allocator, value->len); 1825 | if (interval->name.data == NULL) { 1826 | return NGX_CONF_ERROR; 1827 | } 1828 | 1829 | ngx_memcpy(interval->name.data, value->data, value->len); 1830 | interval->name.len = value->len; 1831 | 1832 | if (ngx_http_stat_parse_time(ctx, value, &interval->value) 1833 | == NGX_CONF_ERROR) 1834 | { 1835 | return NGX_CONF_ERROR; 1836 | } 1837 | 1838 | return NGX_CONF_OK; 1839 | } 1840 | 1841 | 1842 | static 1843 | char * 1844 | ngx_http_stat_param_arg_percentile(ngx_http_stat_ctx_t *ctx, void *data, 1845 | ngx_str_t *value) 1846 | { 1847 | ngx_http_stat_param_t *param; 1848 | ngx_uint_t i, s, *percentile; 1849 | ngx_int_t p; 1850 | 1851 | param = (ngx_http_stat_param_t*)data; 1852 | s = 0; 1853 | 1854 | for (i = 0; i <= value->len; i++) { 1855 | 1856 | if (i == value->len || value->data[i] == '|') { 1857 | 1858 | if (i == s) { 1859 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1860 | "stat param percentile is empty"); 1861 | return NGX_CONF_ERROR; 1862 | } 1863 | 1864 | p = ngx_atoi(&value->data[s], i - s); 1865 | if (p == NGX_ERROR || p <= 0 || p > 100) { 1866 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 1867 | "stat param percentile is invalid"); 1868 | return NGX_CONF_ERROR; 1869 | } 1870 | 1871 | percentile = ngx_http_stat_array_push(param->percentiles); 1872 | if (percentile == NULL) { 1873 | return NGX_CONF_ERROR; 1874 | } 1875 | 1876 | *percentile = p; 1877 | 1878 | s = i + 1; 1879 | } 1880 | } 1881 | 1882 | return NGX_CONF_OK; 1883 | } 1884 | 1885 | 1886 | static 1887 | ngx_uint_t 1888 | ngx_http_stat_add_split(ngx_conf_t *cf, ngx_str_t *name) 1889 | { 1890 | ngx_http_stat_main_conf_t *smcf; 1891 | ngx_array_t *splits; 1892 | ngx_uint_t s, i; 1893 | ngx_str_t *split; 1894 | 1895 | smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_stat_module); 1896 | splits = smcf->splits; 1897 | s = SPLIT_EMPTY; 1898 | 1899 | for (i = 0; i < splits->nelts; i++) { 1900 | split = &((ngx_str_t*)splits->elts)[i]; 1901 | if (split->len == name->len && 1902 | ngx_strncmp(split->data, name->data, name->len) == 0) 1903 | { 1904 | s = i; 1905 | break; 1906 | } 1907 | } 1908 | 1909 | if (s == SPLIT_EMPTY) { 1910 | 1911 | s = splits->nelts; 1912 | split = ngx_array_push(splits); 1913 | if (split == NULL) { 1914 | return SPLIT_EMPTY; 1915 | } 1916 | 1917 | split->data = ngx_pstrdup(cf->pool, (ngx_str_t*)name); 1918 | if (split->data == NULL) { 1919 | return SPLIT_EMPTY; 1920 | } 1921 | 1922 | split->len = name->len; 1923 | } 1924 | 1925 | return s; 1926 | } 1927 | 1928 | 1929 | static 1930 | char * 1931 | ngx_http_stat_add_default_data(ngx_conf_t *cf, ngx_array_t *datas, 1932 | ngx_str_t *location, ngx_array_t *template, 1933 | ngx_array_t *params, ngx_http_complex_value_t *filter) 1934 | { 1935 | ngx_str_t *variables[] = {location}; 1936 | ngx_str_t split; 1937 | 1938 | if (template->nelts == 0) { 1939 | return NGX_CONF_OK; 1940 | } 1941 | 1942 | split.len = ngx_http_stat_template_len(template, variables); 1943 | if (split.len == 0) { 1944 | return NGX_CONF_ERROR; 1945 | } 1946 | 1947 | split.data = ngx_palloc(cf->pool, split.len); 1948 | if (split.data == NULL) { 1949 | return NGX_CONF_ERROR; 1950 | } 1951 | 1952 | ngx_http_stat_template_execute(split.data, split.len, 1953 | template, variables); 1954 | 1955 | return ngx_http_stat_add_data(cf, datas, &split, params, filter); 1956 | } 1957 | 1958 | 1959 | static 1960 | char * 1961 | ngx_http_stat_add_data(ngx_conf_t *cf, ngx_array_t *datas, 1962 | ngx_str_t *split, ngx_array_t *params, 1963 | ngx_http_complex_value_t *filter) 1964 | { 1965 | ngx_http_stat_ctx_t ctx; 1966 | ngx_http_stat_main_conf_t *smcf; 1967 | ngx_uint_t s, i, p; 1968 | ngx_http_stat_data_t *data; 1969 | ngx_http_stat_param_t *param; 1970 | ngx_http_stat_source_t *source; 1971 | 1972 | ctx = ngx_http_stat_ctx_from_config(cf); 1973 | smcf = ctx.smcf; 1974 | 1975 | s = ngx_http_stat_add_split(cf, split); 1976 | if (s == SPLIT_EMPTY) { 1977 | return NGX_CONF_ERROR; 1978 | } 1979 | 1980 | data = ngx_array_push(datas); 1981 | if (data == NULL) { 1982 | return NGX_CONF_ERROR; 1983 | } 1984 | 1985 | if (ngx_http_stat_init_data(&ctx, data) == NGX_ERROR) { 1986 | return NGX_CONF_ERROR; 1987 | } 1988 | 1989 | if (params->nelts == 0) { 1990 | params = smcf->default_params; 1991 | } 1992 | 1993 | for (i = 0; i < params->nelts; i++) { 1994 | 1995 | p = ((ngx_uint_t*)params->elts)[i]; 1996 | 1997 | param = &((ngx_http_stat_param_t*)smcf->storage->params->elts)[p]; 1998 | 1999 | if (param->source != SOURCE_INTERNAL) { 2000 | 2001 | source = &((ngx_http_stat_source_t*) smcf->sources->elts)[param->source]; 2002 | 2003 | if (source->type != 0) { 2004 | 2005 | if ((cf->cmd_type & NGX_HTTP_MAIN_CONF) && 2006 | !(source->type & NGX_HTTP_MAIN_CONF)) 2007 | { 2008 | continue; 2009 | } 2010 | 2011 | if ((cf->cmd_type & NGX_HTTP_SRV_CONF) && 2012 | !(source->type & NGX_HTTP_SRV_CONF)) 2013 | { 2014 | continue; 2015 | } 2016 | 2017 | if ((cf->cmd_type & NGX_HTTP_LOC_CONF) && 2018 | !(source->type & NGX_HTTP_LOC_CONF)) 2019 | { 2020 | continue; 2021 | } 2022 | } 2023 | } 2024 | 2025 | ngx_http_stat_add_param_to_data(&ctx, s, p, data); 2026 | } 2027 | 2028 | data->filter = (ngx_http_complex_value_t*) filter; 2029 | 2030 | return NGX_CONF_OK; 2031 | } 2032 | 2033 | 2034 | static 2035 | ngx_uint_t 2036 | ngx_http_stat_get_source(ngx_http_stat_ctx_t *ctx, ngx_str_t *name) 2037 | { 2038 | ngx_http_stat_main_conf_t *smcf; 2039 | ngx_http_stat_source_t *source; 2040 | ngx_uint_t c; 2041 | ngx_regex_compile_t rc; 2042 | ngx_http_stat_source_t *new_source; 2043 | 2044 | if (ctx->phase != PHASE_CONFIG) { 2045 | return NGX_ERROR; 2046 | } 2047 | 2048 | smcf = ctx->smcf; 2049 | 2050 | for (c = 0; c < smcf->sources->nelts; c++) { 2051 | 2052 | source = &((ngx_http_stat_source_t*) smcf->sources->elts)[c]; 2053 | 2054 | if (source->name.len == name->len && 2055 | ngx_strncmp(source->name.data, name->data, name->len) == 0) 2056 | { 2057 | return c; 2058 | } 2059 | } 2060 | 2061 | for (c = 0; c < ARR_SIZE(ngx_http_stat_sources); c++) { 2062 | 2063 | source = &ngx_http_stat_sources[c]; 2064 | 2065 | if (!source->re) { 2066 | if (source->name.len == name->len && 2067 | ngx_strncmp(source->name.data, name->data, name->len) == 0) 2068 | { 2069 | break; 2070 | } 2071 | } else { 2072 | 2073 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); 2074 | rc.pool = ctx->pool; 2075 | rc.pattern = source->name; 2076 | 2077 | if (ngx_regex_compile(&rc) != NGX_OK) { 2078 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 2079 | "stat can't compile regex"); 2080 | return NGX_ERROR; 2081 | } 2082 | 2083 | if (ngx_regex_exec(rc.regex, name, NULL, 0) >= 0) { 2084 | break; 2085 | } 2086 | } 2087 | } 2088 | 2089 | if (c == ARR_SIZE(ngx_http_stat_sources)) { 2090 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 2091 | "stat unknow param \"%V\"", name); 2092 | return NGX_ERROR; 2093 | } 2094 | 2095 | new_source = ngx_array_push(smcf->sources); 2096 | if (new_source == NULL) { 2097 | return NGX_ERROR; 2098 | } 2099 | *new_source = ngx_http_stat_sources[c]; 2100 | new_source->name = *name; 2101 | 2102 | return smcf->sources->nelts - 1; 2103 | } 2104 | 2105 | 2106 | static 2107 | ngx_http_stat_param_t 2108 | ngx_http_stat_create_param(ngx_http_stat_ctx_t *ctx, ngx_uint_t c) 2109 | { 2110 | ngx_http_stat_main_conf_t *smcf; 2111 | ngx_http_stat_source_t *source; 2112 | ngx_http_stat_param_t param; 2113 | 2114 | smcf = ctx->smcf; 2115 | source = &((ngx_http_stat_source_t*) smcf->sources->elts)[c]; 2116 | 2117 | ngx_memzero(¶m, sizeof(ngx_http_stat_param_t)); 2118 | 2119 | param.name = source->name; 2120 | param.source = c; 2121 | param.aggregate = source->aggregate; 2122 | 2123 | return param; 2124 | } 2125 | 2126 | static 2127 | char * 2128 | ngx_http_stat_parse_params(ngx_http_stat_ctx_t *ctx, ngx_str_t *value, 2129 | ngx_array_t *params) 2130 | { 2131 | ngx_http_stat_main_conf_t *smcf; 2132 | ngx_uint_t i, s, q, p, c; 2133 | ngx_http_stat_param_t param; 2134 | ngx_str_t default_params, name; 2135 | ngx_int_t percentile; 2136 | 2137 | smcf = ctx->smcf; 2138 | 2139 | s = 0; 2140 | q = NGX_CONF_UNSET_UINT; 2141 | 2142 | for (i = 0; i <= value->len; i++) { 2143 | 2144 | if (i == value->len || value->data[i] == '|') { 2145 | 2146 | if (i == s) { 2147 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 2148 | "stat params is empty"); 2149 | return NGX_CONF_ERROR; 2150 | } 2151 | 2152 | if (i - s == 1 && value->data[s] == '*') { 2153 | 2154 | if (params == smcf->default_params) { 2155 | 2156 | ngx_str_set(&default_params, DEFAULT_PARAMS); 2157 | 2158 | if (ngx_http_stat_parse_params(ctx, 2159 | &default_params, params) == NGX_CONF_ERROR) 2160 | { 2161 | return NGX_CONF_ERROR; 2162 | } 2163 | 2164 | } else { 2165 | 2166 | for (p = 0; p < smcf->default_params->nelts; p++) { 2167 | 2168 | c = ((ngx_uint_t*)smcf->default_params->elts)[p]; 2169 | if (ngx_http_stat_add_param_to_params(ctx, c, 2170 | params) != NGX_CONF_OK) 2171 | { 2172 | return NGX_CONF_ERROR; 2173 | } 2174 | } 2175 | } 2176 | 2177 | } else { 2178 | 2179 | if (q == NGX_CONF_UNSET_UINT) { 2180 | q = i; 2181 | } 2182 | 2183 | name.data = &value->data[s]; 2184 | name.len = q - s; 2185 | 2186 | c = ngx_http_stat_get_source(ctx, &name); 2187 | if ((ngx_int_t) c == NGX_ERROR) { 2188 | return NGX_CONF_ERROR; 2189 | } 2190 | 2191 | param = ngx_http_stat_create_param(ctx, c); 2192 | 2193 | if (q != i) { 2194 | 2195 | percentile = ngx_atoi(&value->data[q + 1], i - q - 1); 2196 | if (percentile == NGX_ERROR || 2197 | percentile <= 0 || 2198 | percentile > 100) 2199 | { 2200 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 2201 | "stat bad param \"%*s\"", i - s, 2202 | &value->data[s]); 2203 | return NGX_CONF_ERROR; 2204 | } 2205 | 2206 | param.percentile = percentile; 2207 | } 2208 | 2209 | p = ngx_http_stat_add_param_to_config(ctx, ¶m); 2210 | if ((ngx_int_t) p == NGX_ERROR) { 2211 | return NGX_CONF_ERROR; 2212 | } 2213 | 2214 | if (ngx_http_stat_add_param_to_params(ctx, p, params) 2215 | != NGX_CONF_OK) 2216 | { 2217 | return NGX_CONF_ERROR; 2218 | } 2219 | } 2220 | 2221 | s = i + 1; 2222 | q = NGX_CONF_UNSET_UINT; 2223 | } 2224 | 2225 | if (value->data[i] == '/') { 2226 | q = i; 2227 | } 2228 | } 2229 | 2230 | return NGX_CONF_OK; 2231 | } 2232 | 2233 | 2234 | static 2235 | char * 2236 | ngx_http_stat_parse_string(ngx_http_stat_ctx_t *ctx, ngx_str_t *value, 2237 | ngx_str_t *result) 2238 | { 2239 | ngx_http_stat_allocator_t *allocator; 2240 | 2241 | allocator = ctx->storage->allocator; 2242 | 2243 | result->data = ngx_http_stat_allocator_alloc(allocator, value->len); 2244 | if (result->data == NULL) { 2245 | return NGX_CONF_ERROR; 2246 | } 2247 | 2248 | ngx_memcpy(result->data, value->data, value->len); 2249 | result->len = value->len; 2250 | 2251 | return NGX_CONF_OK; 2252 | } 2253 | 2254 | 2255 | static 2256 | char * 2257 | ngx_http_stat_parse_size(ngx_http_stat_ctx_t *ctx, ngx_str_t *value, 2258 | ngx_uint_t *result) 2259 | { 2260 | ngx_uint_t len; 2261 | 2262 | len = 0; 2263 | 2264 | while (len < value->len && value->data[len] >= '0' && 2265 | value->data[len] <= '9') 2266 | { 2267 | len++; 2268 | } 2269 | 2270 | *result = (ngx_uint_t) ngx_atoi(value->data, len); 2271 | if (*result == (ngx_uint_t) NGX_ERROR) { 2272 | return NGX_CONF_ERROR; 2273 | } 2274 | 2275 | if (len + 1 == value->len) { 2276 | 2277 | switch (value->data[len]) { 2278 | case 'b': 2279 | *result *= 1; 2280 | break; 2281 | case 'k': 2282 | *result *= 1024; 2283 | break; 2284 | case 'm': 2285 | *result *= 1024 * 1024; 2286 | break; 2287 | default: 2288 | return NGX_CONF_ERROR; 2289 | } 2290 | 2291 | len++; 2292 | } 2293 | 2294 | if (len != value->len) { 2295 | return NGX_CONF_ERROR; 2296 | } 2297 | 2298 | return NGX_CONF_OK; 2299 | } 2300 | 2301 | 2302 | static 2303 | char * 2304 | ngx_http_stat_parse_time(ngx_http_stat_ctx_t *ctx, ngx_str_t *value, 2305 | ngx_uint_t *result) 2306 | { 2307 | ngx_uint_t len; 2308 | 2309 | len = 0; 2310 | while (len < value->len && value->data[len] >= '0' && 2311 | value->data[len] <= '9') 2312 | { 2313 | len++; 2314 | } 2315 | 2316 | *result = (ngx_uint_t) ngx_atoi(value->data, len); 2317 | if (*result == (ngx_uint_t) NGX_ERROR) { 2318 | return NGX_CONF_ERROR; 2319 | } 2320 | 2321 | if (len + 1 == value->len) { 2322 | 2323 | switch (value->data[len]) { 2324 | case 's': 2325 | *result *= 1; 2326 | break; 2327 | case 'm': 2328 | *result *= 60; 2329 | break; 2330 | default: 2331 | return NGX_CONF_ERROR; 2332 | } 2333 | 2334 | len++; 2335 | } 2336 | 2337 | if (len != value->len) { 2338 | return NGX_CONF_ERROR; 2339 | } 2340 | 2341 | return NGX_CONF_OK; 2342 | } 2343 | 2344 | /** Variables, Stats and metric API {{{ */ 2345 | char * 2346 | ngx_http_stat_template_compile(ngx_http_stat_ctx_t *ctx, 2347 | ngx_array_t *template, ngx_http_stat_template_arg_t *args, 2348 | ngx_uint_t nargs, ngx_str_t *value) 2349 | { 2350 | typedef enum { 2351 | TEMPLATE_STATE_ERROR = -1, 2352 | TEMPLATE_STATE_NONE, 2353 | TEMPLATE_STATE_VAR, 2354 | TEMPLATE_STATE_BRACKET_VAR, 2355 | TEMPLATE_STATE_VAR_START, 2356 | TEMPLATE_STATE_BRACKET_VAR_START, 2357 | TEMPLATE_STATE_BRACKET_VAR_END, 2358 | TEMPLATE_STATE_NOP, 2359 | } ngx_http_stat_template_parser_state_t; 2360 | 2361 | typedef enum { 2362 | TEMPLATE_LEXEM_ERROR = -1, 2363 | TEMPLATE_LEXEM_VAR, 2364 | TEMPLATE_LEXEM_BRACKET_OPEN, 2365 | TEMPLATE_LEXEM_BRACKET_CLOSE, 2366 | TEMPLATE_LEXEM_ALNUM, 2367 | TEMPLATE_LEXEM_OTHER, 2368 | TEMPLATE_LEXEM_END, 2369 | } ngx_http_stat_template_parser_lexem_t; 2370 | 2371 | ngx_http_stat_template_parser_state_t parser[ 2372 | TEMPLATE_STATE_BRACKET_VAR_END + 1][TEMPLATE_LEXEM_END + 1] = { 2373 | { 1, 6, 6, 6, 6, 0}, 2374 | { 0, 2, -1, 3, -1, -1}, 2375 | {-1, -1, -1, 4, -1, -1}, 2376 | { 0, 0, 0, 6, 0, 0}, 2377 | {-1, -1, 5, 6, -1, -1}, 2378 | { 0, 0, 0, 0, 0, 0}, 2379 | }; 2380 | 2381 | ngx_http_stat_allocator_t *allocator; 2382 | ngx_uint_t i, s, find, a; 2383 | ngx_int_t new_state; 2384 | ngx_http_stat_template_parser_state_t state; 2385 | ngx_http_stat_template_parser_lexem_t lexem; 2386 | ngx_http_stat_template_t *arg; 2387 | 2388 | allocator = ctx->storage->allocator; 2389 | s = 0; 2390 | state = TEMPLATE_STATE_NONE; 2391 | 2392 | for (i = 0; i <= value->len; i++) { 2393 | 2394 | if (i == value->len) { 2395 | lexem = TEMPLATE_LEXEM_END; 2396 | } else if (value->data[i] == '$') { 2397 | lexem = TEMPLATE_LEXEM_VAR; 2398 | } else if (value->data[i] == '(') { 2399 | lexem = TEMPLATE_LEXEM_BRACKET_OPEN; 2400 | } else if (value->data[i] == ')') { 2401 | lexem = TEMPLATE_LEXEM_BRACKET_CLOSE; 2402 | } else if (isalnum(value->data[i])) { 2403 | lexem = TEMPLATE_LEXEM_ALNUM; 2404 | } else { 2405 | lexem = TEMPLATE_LEXEM_OTHER; 2406 | } 2407 | 2408 | new_state = parser[state][lexem]; 2409 | 2410 | if (new_state == TEMPLATE_LEXEM_ERROR) { 2411 | return NGX_CONF_ERROR; 2412 | } 2413 | 2414 | if (new_state != TEMPLATE_STATE_NOP) { 2415 | 2416 | if (i != s && (state == TEMPLATE_STATE_NONE || 2417 | state == TEMPLATE_STATE_VAR_START || 2418 | state == TEMPLATE_STATE_BRACKET_VAR_START)) 2419 | { 2420 | arg = ngx_array_push(template); 2421 | if (arg == NULL) { 2422 | return NGX_CONF_ERROR; 2423 | } 2424 | 2425 | if (state == TEMPLATE_STATE_NONE) { 2426 | 2427 | arg->data.data = ngx_http_stat_allocator_alloc( 2428 | allocator, i - s); 2429 | if (arg->data.data == NULL) { 2430 | return NGX_CONF_ERROR; 2431 | } 2432 | 2433 | ngx_memcpy(arg->data.data, value->data + s, i - s); 2434 | arg->data.len = i - s; 2435 | arg->variable = 0; 2436 | 2437 | } else if (state == TEMPLATE_STATE_VAR_START || 2438 | state == TEMPLATE_STATE_BRACKET_VAR_START) 2439 | { 2440 | find = 0; 2441 | 2442 | for (a = 0; a < nargs; a++) { 2443 | 2444 | if (args[a].name.len == (i - s) && 2445 | ngx_strncmp(args[a].name.data, 2446 | &value->data[s], i - s) == 0) 2447 | { 2448 | find = 1; 2449 | arg->variable = args[a].variable; 2450 | arg->data.data = NULL; 2451 | arg->data.len = 0; 2452 | break; 2453 | } 2454 | } 2455 | 2456 | if (!find) { 2457 | ngx_log_error(NGX_LOG_ERR, ctx->log, 0, 2458 | "stat unknow template arg \"%*s\"", 2459 | i - s, &value->data[s]); 2460 | return NGX_CONF_ERROR; 2461 | } 2462 | } 2463 | } 2464 | 2465 | s = i; 2466 | state = new_state; 2467 | } 2468 | } 2469 | 2470 | return NGX_CONF_OK; 2471 | } 2472 | 2473 | 2474 | u_char * 2475 | ngx_http_stat_template_execute(u_char* buffer, ngx_uint_t buffer_size, 2476 | ngx_array_t *template, ngx_str_t *variables[]) 2477 | { 2478 | u_char *b; 2479 | ngx_uint_t i; 2480 | ngx_http_stat_template_t *arg; 2481 | ngx_str_t *data; 2482 | 2483 | b = buffer; 2484 | 2485 | for (i = 0; i < template->nelts; i++) { 2486 | 2487 | arg = &((ngx_http_stat_template_t*) template->elts)[i]; 2488 | 2489 | if (arg->data.len) { 2490 | data = &arg->data; 2491 | } else { 2492 | data = variables[arg->variable]; 2493 | } 2494 | 2495 | b = ngx_snprintf(b, buffer_size - (b - buffer), "%V", data); 2496 | } 2497 | 2498 | return b; 2499 | } 2500 | 2501 | 2502 | ngx_uint_t 2503 | ngx_http_stat_template_len(ngx_array_t *template, ngx_str_t *variables[]) 2504 | { 2505 | ngx_uint_t len, i; 2506 | ngx_http_stat_template_t *arg; 2507 | 2508 | len = 0; 2509 | 2510 | for (i = 0; i < template->nelts; i++) { 2511 | 2512 | arg = &((ngx_http_stat_template_t*) template->elts)[i]; 2513 | 2514 | if (arg->data.len) { 2515 | len += arg->data.len; 2516 | } else { 2517 | len += variables[arg->variable]->len; 2518 | } 2519 | } 2520 | 2521 | return len; 2522 | } 2523 | /** }}} */ 2524 | 2525 | static 2526 | ngx_http_complex_value_t * 2527 | ngx_http_stat_complex_compile(ngx_conf_t *cf, ngx_str_t *value) 2528 | { 2529 | ngx_http_compile_complex_value_t ccv; 2530 | 2531 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 2532 | 2533 | ccv.cf = cf; 2534 | ccv.value = value; 2535 | ccv.complex_value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); 2536 | if (ccv.complex_value == NULL) { 2537 | return NULL; 2538 | } 2539 | 2540 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 2541 | return NULL; 2542 | } 2543 | 2544 | return ccv.complex_value; 2545 | } 2546 | 2547 | 2548 | static 2549 | ngx_int_t 2550 | ngx_http_stat_shared_init(ngx_shm_zone_t *shm_zone, void *data) 2551 | { 2552 | ngx_http_stat_main_conf_t *smcf = shm_zone->data; 2553 | 2554 | ngx_uint_t shared_required_size, buffer_required_size, m, 2555 | s; 2556 | ngx_slab_pool_t *shpool; 2557 | ngx_http_stat_storage_t *storage; 2558 | ngx_http_stat_allocator_t *allocator; 2559 | u_char *accs, *stts; 2560 | ngx_http_stat_metric_t *metric; 2561 | ngx_http_stat_statistic_t *statistic; 2562 | ngx_http_stat_param_t *param; 2563 | 2564 | if (data) { 2565 | ngx_log_error(NGX_LOG_ERR, shm_zone->shm.log, 0, 2566 | "stat shared memory data set"); 2567 | return NGX_ERROR; 2568 | } 2569 | 2570 | shared_required_size = 2571 | 2 * 2572 | (sizeof(ngx_slab_pool_t) + 2573 | sizeof(ngx_http_stat_storage_t) + 2574 | sizeof(ngx_array_t) * 4 + 2575 | sizeof(ngx_http_stat_metric_t) * (smcf->storage->metrics->nelts) + 2576 | sizeof(ngx_http_stat_statistic_t) * (smcf->storage->statistics->nelts) + 2577 | sizeof(ngx_http_stat_param_t) * (smcf->storage->params->nelts) + 2578 | sizeof(ngx_http_stat_internal_t) * (smcf->storage->internals->nelts) + 2579 | sizeof(ngx_http_stat_acc_t) * (smcf->storage->max_interval + 1) * 2580 | smcf->storage->metrics->nelts + 2581 | sizeof(ngx_http_stat_stt_t) * smcf->storage->statistics->nelts); 2582 | 2583 | if (shared_required_size > shm_zone->shm.size) { 2584 | ngx_log_error(NGX_LOG_ERR, shm_zone->shm.log, 0, 2585 | "stat too small shared memory (minimum size is %uzb)", 2586 | shared_required_size); 2587 | return NGX_ERROR; 2588 | } 2589 | 2590 | /* 2591 | * 128 is the approximate size of the one record 2592 | */ 2593 | buffer_required_size = (smcf->intervals->nelts * 2594 | smcf->storage->metrics->nelts + smcf->storage->statistics->nelts) * 2595 | 128; 2596 | if (buffer_required_size > smcf->buffer_size) { 2597 | ngx_log_error(NGX_LOG_ERR, shm_zone->shm.log, 0, 2598 | "stat too small buffer size (minimum size is %uzb)", 2599 | buffer_required_size); 2600 | return NGX_ERROR; 2601 | } 2602 | 2603 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 2604 | 2605 | if (shm_zone->shm.exists) { 2606 | ngx_log_error(NGX_LOG_ERR, shm_zone->shm.log, 0, 2607 | "stat shared memory exists"); 2608 | return NGX_ERROR; 2609 | } 2610 | 2611 | storage = ngx_slab_alloc(shpool, sizeof(ngx_http_stat_storage_t)); 2612 | if (storage == NULL) { 2613 | return NGX_ERROR; 2614 | } 2615 | 2616 | shpool->data = storage; 2617 | 2618 | ngx_memzero(storage, sizeof(ngx_http_stat_storage_t)); 2619 | 2620 | storage->max_interval = smcf->storage->max_interval; 2621 | 2622 | storage->start_time = ngx_time(); 2623 | storage->last_time = storage->start_time; 2624 | storage->event_time = storage->start_time; 2625 | 2626 | allocator = ngx_slab_alloc(shpool, sizeof(ngx_http_stat_allocator_t)); 2627 | if (allocator == NULL) { 2628 | return NGX_ERROR; 2629 | } 2630 | 2631 | ngx_http_stat_allocator_init(allocator, 2632 | shpool, ngx_http_stat_allocator_slab_alloc, 2633 | ngx_http_stat_allocator_slab_free); 2634 | 2635 | storage->allocator = allocator; 2636 | storage->metrics = ngx_http_stat_array_copy(storage->allocator, 2637 | smcf->storage->metrics); 2638 | storage->statistics = ngx_http_stat_array_copy(storage->allocator, 2639 | smcf->storage->statistics); 2640 | storage->params = ngx_http_stat_array_copy(storage->allocator, 2641 | smcf->storage->params); 2642 | storage->internals = ngx_http_stat_array_copy(storage->allocator, 2643 | smcf->storage->internals); 2644 | 2645 | if (!storage->metrics || !storage->statistics || !storage->params || 2646 | !storage->internals) 2647 | { 2648 | return NGX_ERROR; 2649 | } 2650 | 2651 | accs = ngx_slab_calloc(shpool, sizeof(ngx_http_stat_acc_t) * 2652 | (smcf->storage->max_interval + 1) * smcf->storage->metrics->nelts); 2653 | if (accs == NULL) { 2654 | return NGX_ERROR; 2655 | } 2656 | 2657 | stts = ngx_slab_calloc(shpool, 2658 | sizeof(ngx_http_stat_stt_t) * smcf->storage->statistics->nelts); 2659 | if (stts == NULL) { 2660 | return NGX_ERROR; 2661 | } 2662 | 2663 | for (m = 0; m < storage->metrics->nelts; m++) { 2664 | 2665 | metric = &((ngx_http_stat_metric_t *) storage->metrics->elts)[m]; 2666 | metric->acc = (ngx_http_stat_acc_t *)(accs + 2667 | sizeof(ngx_http_stat_acc_t) * (smcf->storage->max_interval + 1) * 2668 | m); 2669 | } 2670 | 2671 | for (s = 0; s < storage->statistics->nelts; s++) { 2672 | 2673 | statistic = &(((ngx_http_stat_statistic_t*) 2674 | storage->statistics->elts)[s]); 2675 | 2676 | param = &((ngx_http_stat_param_t*) 2677 | smcf->storage->params)[statistic->param]; 2678 | 2679 | statistic->stt = (ngx_http_stat_stt_t*)( 2680 | stts + sizeof(ngx_http_stat_stt_t) * s); 2681 | 2682 | ngx_http_stat_statistic_init(statistic->stt, param->percentile); 2683 | } 2684 | 2685 | return NGX_OK; 2686 | } 2687 | 2688 | 2689 | static 2690 | double * 2691 | ngx_http_stat_get_sources_values(ngx_http_stat_main_conf_t *smcf, 2692 | ngx_http_request_t *r) 2693 | { 2694 | double *values; 2695 | ngx_uint_t c; 2696 | ngx_http_stat_source_t *source; 2697 | 2698 | values = ngx_palloc(r->pool, sizeof(double) * smcf->sources->nelts); 2699 | if (values == NULL) { 2700 | return NULL; 2701 | } 2702 | 2703 | for (c = 0; c < smcf->sources->nelts; c++) { 2704 | 2705 | source = &((ngx_http_stat_source_t*)smcf->sources->elts)[c]; 2706 | if (source->get) { 2707 | values[c] = source->get(source, r); 2708 | } 2709 | } 2710 | 2711 | return values; 2712 | } 2713 | 2714 | 2715 | static 2716 | void 2717 | ngx_http_stat_add_metric(ngx_http_request_t *r, 2718 | ngx_http_stat_storage_t *storage, 2719 | ngx_http_stat_metric_t *metric, time_t ts, double value) 2720 | { 2721 | ngx_uint_t a = ((ts - storage->start_time) % (storage->max_interval + 1)); 2722 | ngx_http_stat_acc_t *acc = &metric->acc[a]; 2723 | 2724 | acc->count++; 2725 | acc->value += value; 2726 | } 2727 | 2728 | 2729 | static 2730 | void 2731 | ngx_http_stat_add_statistic(ngx_http_request_t *r, 2732 | ngx_http_stat_storage_t *storage, 2733 | ngx_http_stat_statistic_t *statistic, time_t ts, double value, 2734 | ngx_uint_t percentile) 2735 | { 2736 | ngx_http_stat_stt_t *stt; 2737 | ngx_uint_t k, i; 2738 | double d, a; 2739 | ngx_int_t s; 2740 | 2741 | stt = statistic->stt; 2742 | 2743 | if (stt->count >= P2_METRIC_COUNT) { 2744 | 2745 | for (k = 0; k < P2_METRIC_COUNT; k++) { 2746 | if (value < stt->q[k]) 2747 | break; 2748 | } 2749 | 2750 | if (k == 0) { 2751 | k = 1; 2752 | stt->q[0] = value; 2753 | } else if (k == P2_METRIC_COUNT) { 2754 | k = 4; 2755 | stt->q[P2_METRIC_COUNT - 1] = value; 2756 | } 2757 | 2758 | for (i = 0; i < P2_METRIC_COUNT; i++) { 2759 | 2760 | if (i >= k) { 2761 | stt->n[i]++; 2762 | } 2763 | stt->np[i] += stt->dn[i]; 2764 | } 2765 | 2766 | for (i = 1; i < P2_METRIC_COUNT - 1; i++) { 2767 | 2768 | d = stt->np[i] - stt->n[i]; 2769 | s = (d >= 0.0) ? 1 : -1; 2770 | 2771 | if ((d >= 1.0 && stt->n[i + 1] - stt->n[i] > 1) || 2772 | (d <= -1.0 && stt->n[i - 1] - stt->n[i] < -1)) 2773 | { 2774 | a = stt->q[i] + (double) s * ((stt->n[i] - stt->n[i - 1] + s) * 2775 | (stt->q[i + 1] - stt->q[i]) / (stt->n[i + 1] - stt->n[i]) + 2776 | (stt->n[i + 1] - stt->n[i] - s) * (stt->q[i] - stt->q[i - 1]) / 2777 | (stt->n[i] - stt->n[i - 1])) / (stt->n[i + 1] - stt->n[i - 1]); 2778 | 2779 | if (a <= stt->q[i - 1] || stt->q[i + 1] <= a) { 2780 | a = stt->q[i] + (double) s * (stt->q[i + s] - stt->q[i]) / 2781 | (stt->n[i + s] - stt->n[i]); 2782 | } 2783 | 2784 | stt->q[i] = a; 2785 | stt->n[i] += s; 2786 | } 2787 | } 2788 | 2789 | } else { 2790 | 2791 | for (i = 0; i < stt->count; i++) { 2792 | if (value < stt->q[i]) { 2793 | break; 2794 | } 2795 | } 2796 | 2797 | if (stt->count != 0 && i < P2_METRIC_COUNT - 1) { 2798 | memmove(&stt->q[i + 1], &stt->q[i], 2799 | (stt->count - i) * sizeof(*stt->q)); 2800 | } 2801 | 2802 | stt->q[i] = value; 2803 | stt->count++; 2804 | } 2805 | } 2806 | 2807 | 2808 | static void 2809 | ngx_http_stat_add_data_values(ngx_http_request_t *r, 2810 | ngx_http_stat_storage_t *storage, time_t ts, 2811 | ngx_http_stat_data_t *data, double *values) 2812 | { 2813 | ngx_uint_t i, m, s; 2814 | ngx_str_t result; 2815 | ngx_http_stat_metric_t *metric; 2816 | ngx_http_stat_param_t *param; 2817 | ngx_http_stat_statistic_t *statistic; 2818 | double value; 2819 | 2820 | if (data->filter) { 2821 | 2822 | if (ngx_http_complex_value(r, data->filter, &result) != NGX_OK) { 2823 | return; 2824 | } 2825 | 2826 | if (result.len == 0 || (result.len == 1 && result.data[0] == '0')) { 2827 | return; 2828 | } 2829 | } 2830 | 2831 | for (i = 0; i < data->metrics->nelts; i++) { 2832 | 2833 | m = ((ngx_uint_t*) data->metrics->elts)[i]; 2834 | metric = &((ngx_http_stat_metric_t*)storage->metrics->elts)[m]; 2835 | param = &((ngx_http_stat_param_t*)storage->params->elts)[metric->param]; 2836 | value = (param->source != SOURCE_INTERNAL) ? values[param->source] : 2837 | values[0]; 2838 | ngx_http_stat_add_metric(r, storage, metric, ts, value); 2839 | } 2840 | 2841 | for (i = 0; i < data->statistics->nelts; i++) { 2842 | 2843 | s = ((ngx_uint_t*)data->statistics->elts)[i]; 2844 | statistic = &((ngx_http_stat_statistic_t*)storage->statistics->elts)[s]; 2845 | param = &((ngx_http_stat_param_t*)storage->params->elts)[statistic->param]; 2846 | value = (param->source != SOURCE_INTERNAL) ? values[param->source] : 2847 | values[0]; 2848 | ngx_http_stat_add_statistic(r, storage, statistic, ts, value, param->percentile); 2849 | } 2850 | } 2851 | 2852 | 2853 | static 2854 | void 2855 | ngx_http_stat_add_datas_values(ngx_http_request_t *r, 2856 | ngx_http_stat_storage_t *storage, time_t ts, 2857 | ngx_array_t *datas, double *values) 2858 | { 2859 | ngx_uint_t i; 2860 | ngx_http_stat_data_t *data; 2861 | 2862 | for (i = 0; i < datas->nelts; i++) { 2863 | data = &((ngx_http_stat_data_t*)datas->elts)[i]; 2864 | ngx_http_stat_add_data_values(r, storage, ts, data, values); 2865 | } 2866 | } 2867 | 2868 | 2869 | static 2870 | ngx_int_t 2871 | ngx_http_stat_handler(ngx_http_request_t *r) 2872 | { 2873 | 2874 | ngx_http_stat_main_conf_t *smcf; 2875 | ngx_http_stat_srv_conf_t *sscf; 2876 | ngx_http_stat_loc_conf_t *slcf; 2877 | ngx_slab_pool_t *shpool; 2878 | ngx_http_stat_storage_t *storage; 2879 | double *values; 2880 | time_t ts; 2881 | 2882 | smcf = ngx_http_get_module_main_conf(r, ngx_http_stat_module); 2883 | sscf = ngx_http_get_module_srv_conf(r, ngx_http_stat_module); 2884 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_stat_module); 2885 | 2886 | if (!smcf->enable) { 2887 | return NGX_OK; 2888 | } 2889 | 2890 | if (smcf->datas->nelts == 0 && 2891 | sscf->datas->nelts == 0 && 2892 | slcf->datas->nelts == 0) 2893 | { 2894 | return NGX_OK; 2895 | } 2896 | 2897 | shpool = (ngx_slab_pool_t*) smcf->shared->shm.addr; 2898 | storage = (ngx_http_stat_storage_t*) shpool->data; 2899 | 2900 | ts = ngx_time(); 2901 | 2902 | values = ngx_http_stat_get_sources_values(smcf, r); 2903 | if (values == NULL) { 2904 | return NGX_OK; 2905 | } 2906 | 2907 | ngx_shmtx_lock(&shpool->mutex); 2908 | ngx_http_stat_gc(smcf, ts); 2909 | 2910 | if (r == r->main) { 2911 | ngx_http_stat_add_datas_values(r, storage, ts, smcf->datas, values); 2912 | ngx_http_stat_add_datas_values(r, storage, ts, sscf->datas, values); 2913 | } 2914 | 2915 | ngx_http_stat_add_datas_values(r, storage, ts, slcf->datas, values); 2916 | 2917 | ngx_shmtx_unlock(&shpool->mutex); 2918 | 2919 | return NGX_OK; 2920 | } 2921 | 2922 | 2923 | ngx_int_t 2924 | ngx_http_stat(ngx_http_request_t *r, ngx_str_t *name, double value, 2925 | char *config) 2926 | { 2927 | ngx_http_stat_ctx_t ctx; 2928 | ngx_http_stat_main_conf_t *smcf; 2929 | ngx_slab_pool_t *shpool; 2930 | ngx_http_stat_storage_t *storage; 2931 | time_t ts; 2932 | ngx_int_t found, i; 2933 | ngx_array_t *args; 2934 | ngx_http_stat_param_t param; 2935 | ngx_http_stat_internal_t *internal; 2936 | 2937 | ctx = ngx_http_stat_ctx_from_request(r); 2938 | smcf = ctx.smcf; 2939 | 2940 | if (!smcf->enable) { 2941 | return NGX_OK; 2942 | } 2943 | 2944 | shpool = (ngx_slab_pool_t*) smcf->shared->shm.addr; 2945 | storage = (ngx_http_stat_storage_t*) shpool->data; 2946 | 2947 | ts = ngx_time(); 2948 | 2949 | ngx_shmtx_lock(&shpool->mutex); 2950 | 2951 | ngx_http_stat_gc(smcf, ts); 2952 | 2953 | found = 0; 2954 | i = ngx_http_stat_search_param(storage->internals, name, &found); 2955 | 2956 | if (!found) { 2957 | 2958 | if (!config || storage->allocator->nomemory) { 2959 | ngx_shmtx_unlock(&shpool->mutex); 2960 | return NGX_OK; 2961 | } 2962 | 2963 | args = ngx_http_stat_create_param_args(r->pool, name, config); 2964 | if (args == NULL) { 2965 | ngx_shmtx_unlock(&shpool->mutex); 2966 | return NGX_ERROR; 2967 | } 2968 | 2969 | if (ngx_http_stat_parse_param_args(&ctx, args, ¶m) != NGX_OK) { 2970 | ngx_shmtx_unlock(&shpool->mutex); 2971 | return NGX_ERROR; 2972 | } 2973 | 2974 | i = ngx_http_stat_add_internal(&ctx, ¶m); 2975 | 2976 | if (i == NGX_ERROR) { 2977 | ngx_shmtx_unlock(&shpool->mutex); 2978 | return NGX_ERROR; 2979 | } 2980 | } 2981 | 2982 | internal = &((ngx_http_stat_internal_t*) storage->internals->elts)[i]; 2983 | ngx_http_stat_add_data_values(r, storage, ts, &internal->data, &value); 2984 | 2985 | ngx_shmtx_unlock(&shpool->mutex); 2986 | 2987 | return NGX_OK; 2988 | } 2989 | 2990 | 2991 | void 2992 | ngx_http_stat_gc(ngx_http_stat_main_conf_t *smcf, time_t ts) 2993 | { 2994 | ngx_slab_pool_t *shpool; 2995 | ngx_http_stat_storage_t *storage; 2996 | ngx_uint_t m, a; 2997 | ngx_http_stat_metric_t *metric; 2998 | ngx_http_stat_acc_t *acc; 2999 | 3000 | shpool = (ngx_slab_pool_t *) smcf->shared->shm.addr; 3001 | storage = (ngx_http_stat_storage_t *) shpool->data; 3002 | 3003 | while ((ngx_uint_t) storage->last_time + storage->max_interval < 3004 | (ngx_uint_t)ts) 3005 | { 3006 | for (m = 0; m < storage->metrics->nelts; m++) { 3007 | 3008 | metric = &(((ngx_http_stat_metric_t*) 3009 | storage->metrics->elts)[m]); 3010 | 3011 | a = ((storage->last_time - storage->start_time) % ( 3012 | storage->max_interval + 1)); 3013 | 3014 | acc = &metric->acc[a]; 3015 | 3016 | acc->value = 0; 3017 | acc->count = 0; 3018 | } 3019 | 3020 | storage->last_time++; 3021 | } 3022 | } 3023 | 3024 | 3025 | static 3026 | double 3027 | ngx_http_stat_source_request_time( ngx_http_stat_source_t *source, 3028 | ngx_http_request_t *r) 3029 | { 3030 | ngx_msec_int_t ms; 3031 | struct timeval tp; 3032 | 3033 | ngx_gettimeofday(&tp); 3034 | 3035 | ms = (ngx_msec_int_t) ((tp.tv_sec - r->start_sec) * 1000 + 3036 | (tp.tv_usec / 1000 - r->start_msec)); 3037 | 3038 | return (double) ms; 3039 | } 3040 | 3041 | 3042 | static 3043 | double 3044 | ngx_http_stat_source_bytes_sent( ngx_http_stat_source_t *source, 3045 | ngx_http_request_t *r) 3046 | { 3047 | return (double) r->connection->sent; 3048 | } 3049 | 3050 | 3051 | static 3052 | double 3053 | ngx_http_stat_source_body_bytes_sent( ngx_http_stat_source_t *source, 3054 | ngx_http_request_t *r) 3055 | { 3056 | off_t length; 3057 | 3058 | length = r->connection->sent - r->header_size; 3059 | length = ngx_max(length, 0); 3060 | 3061 | return (double) length; 3062 | } 3063 | 3064 | static 3065 | double 3066 | ngx_http_stat_source_request_length( ngx_http_stat_source_t *source, 3067 | ngx_http_request_t *r) 3068 | { 3069 | return (double) r->request_length; 3070 | } 3071 | 3072 | 3073 | static 3074 | double 3075 | ngx_http_stat_source_rps(ngx_http_stat_source_t *source, 3076 | ngx_http_request_t *r) 3077 | { 3078 | return 1; 3079 | } 3080 | 3081 | 3082 | static 3083 | double 3084 | ngx_http_stat_source_keepalive_rps(ngx_http_stat_source_t *source, 3085 | ngx_http_request_t *r) 3086 | { 3087 | if (r->connection->requests == 1) { 3088 | return 0; 3089 | } 3090 | return 1; 3091 | } 3092 | 3093 | 3094 | static 3095 | double 3096 | ngx_http_stat_source_response_2xx_rps( ngx_http_stat_source_t *source, 3097 | ngx_http_request_t *r) 3098 | { 3099 | if (r->headers_out.status / 100 == 2) { 3100 | return 1; 3101 | } 3102 | return 0; 3103 | } 3104 | 3105 | 3106 | static 3107 | double 3108 | ngx_http_stat_source_response_3xx_rps( ngx_http_stat_source_t *source, 3109 | ngx_http_request_t *r) 3110 | { 3111 | if (r->headers_out.status / 100 == 3) { 3112 | return 1; 3113 | } 3114 | return 0; 3115 | } 3116 | 3117 | 3118 | static 3119 | double 3120 | ngx_http_stat_source_response_4xx_rps(ngx_http_stat_source_t *source, 3121 | ngx_http_request_t *r) 3122 | { 3123 | if (r->headers_out.status / 100 == 4) { 3124 | return 1; 3125 | } 3126 | return 0; 3127 | } 3128 | 3129 | 3130 | static 3131 | double 3132 | ngx_http_stat_source_response_5xx_rps( ngx_http_stat_source_t *source, 3133 | ngx_http_request_t *r) 3134 | { 3135 | if (r->headers_out.status / 100 == 5) { 3136 | return 1; 3137 | } 3138 | return 0; 3139 | } 3140 | 3141 | 3142 | static 3143 | double 3144 | ngx_http_stat_source_response_xxx_rps(ngx_http_stat_source_t *source, 3145 | ngx_http_request_t *r) 3146 | { 3147 | ngx_uint_t status; 3148 | 3149 | status = ngx_atoi(source->name.data + sizeof("response_") - 1, 3); 3150 | if (r->headers_out.status == status) { 3151 | return 1; 3152 | } 3153 | return 0; 3154 | } 3155 | 3156 | 3157 | static 3158 | double 3159 | ngx_http_stat_source_upstream_cache_status_rps( 3160 | ngx_http_stat_source_t *source, ngx_http_request_t *r) 3161 | { 3162 | #if NGX_HTTP_CACHE 3163 | ngx_str_t param_status, cache_status; 3164 | 3165 | if (r->upstream == NULL || r->upstream->cache_status == 0) { 3166 | return 0; 3167 | } 3168 | 3169 | param_status.data = source->name.data + sizeof("upstream_cache_") - 1; 3170 | param_status.len = source->name.len - (sizeof("upstream_cache_") - 1) - 4; 3171 | 3172 | cache_status = ngx_http_cache_status[r->upstream->cache_status - 1]; 3173 | if (cache_status.len == param_status.len && 3174 | ngx_strncasecmp(cache_status.data, param_status.data, 3175 | param_status.len) == 0) 3176 | { 3177 | return 1; 3178 | } 3179 | #endif /** NGX_HTTP_CACHE */ 3180 | return 0; 3181 | } 3182 | 3183 | /** Acc && Inver funcs API {{{ */ 3184 | double 3185 | ngx_http_stat_aggregate_avg( ngx_http_stat_interval_t *interval, 3186 | ngx_http_stat_acc_t *acc) 3187 | { 3188 | return (acc->count != 0) ? acc->value / acc->count : 0; 3189 | } 3190 | 3191 | 3192 | double 3193 | ngx_http_stat_aggregate_persec( ngx_http_stat_interval_t *interval, 3194 | ngx_http_stat_acc_t *acc) 3195 | { 3196 | return acc->value / interval->value; 3197 | } 3198 | 3199 | 3200 | double 3201 | ngx_http_stat_aggregate_sum( ngx_http_stat_interval_t *interval, 3202 | ngx_http_stat_acc_t *acc) 3203 | { 3204 | return acc->value; 3205 | } 3206 | 3207 | 3208 | void 3209 | ngx_http_stat_statistic_init(ngx_http_stat_stt_t *stt, ngx_uint_t percentile) 3210 | { 3211 | double p; 3212 | ngx_uint_t i; 3213 | 3214 | ngx_memzero(stt, sizeof(ngx_http_stat_stt_t)); 3215 | 3216 | p = (double) percentile / 100; 3217 | 3218 | stt->dn[P2_METRIC_COUNT - 1] = 1; 3219 | stt->dn[P2_METRIC_COUNT / 2] = p; 3220 | 3221 | for (i = 1; i < P2_METRIC_COUNT / 2; i++) { 3222 | stt->dn[i] = (p / (P2_METRIC_COUNT / 2)) * i; 3223 | } 3224 | 3225 | for (i = P2_METRIC_COUNT / 2 + 1; i < P2_METRIC_COUNT - 1; i++) { 3226 | stt->dn[i] = p + ((1 - p) / (P2_METRIC_COUNT - 1 - P2_METRIC_COUNT / 2)) 3227 | * (i - P2_METRIC_COUNT / 2); 3228 | } 3229 | 3230 | for (i = 0; i < P2_METRIC_COUNT; i++) { 3231 | stt->np[i] = (P2_METRIC_COUNT - 1) * stt->dn[i] + 1; 3232 | stt->n[i] = i + 1; 3233 | } 3234 | } 3235 | 3236 | /** }}} */ 3237 | 3238 | /** }}} */ 3239 | 3240 | -------------------------------------------------------------------------------- /src/ngx_http_stat_module.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * (c) BSD-2-Cause, link: https://opensource.org/licenses/BSD-2-Clause 4 | * (c) V. Soshnikov, mailto: dedok.mad@gmail.com 5 | */ 6 | 7 | #ifndef NGX_HTTP_STAT_MODULE_H_INCLUDED 8 | #define NGX_HTTP_STAT_MODULE_H_INCLUDED 1 9 | 10 | #include "ngx_http_stat_array.h" 11 | #include "ngx_http_stat_allocator.h" 12 | 13 | 14 | #define HOST_LEN 256 15 | 16 | #define PHASE_CONFIG 0 17 | #define PHASE_REQUEST 1 18 | 19 | #define SPLIT_EMPTY ((ngx_uint_t) -1) 20 | #define SPLIT_INTERNAL ((ngx_uint_t) - 2) 21 | #define SOURCE_INTERNAL ((ngx_uint_t) - 1) 22 | 23 | #define ARR_SIZE(struct_) \ 24 | (sizeof((struct_)) / sizeof(struct_[0])) 25 | 26 | #define DEFAULT_PARAMS \ 27 | "request_time|" \ 28 | "bytes_sent|" \ 29 | "body_bytes_sent|" \ 30 | "request_length|" \ 31 | "rps|" \ 32 | "keepalive_rps|" \ 33 | "response_2xx_rps|" \ 34 | "response_3xx_rps|" \ 35 | "response_4xx_rps|" \ 36 | "response_5xx_rps" 37 | 38 | #define TEMPLATE_VARIABLES(host, split, param, interval) \ 39 | {host, split, param, interval} 40 | 41 | 42 | /** Shm mem struct */ 43 | typedef struct { 44 | time_t start_time, last_time, event_time; 45 | 46 | ngx_uint_t max_interval; 47 | 48 | ngx_http_stat_allocator_t *allocator; 49 | 50 | ngx_http_stat_array_t *metrics; 51 | ngx_http_stat_array_t *statistics; 52 | 53 | ngx_http_stat_array_t *params; 54 | ngx_http_stat_array_t *internals; 55 | } ngx_http_stat_storage_t; 56 | 57 | 58 | /** Backend */ 59 | typedef struct { 60 | struct sockaddr *sockaddr; 61 | socklen_t socklen; 62 | ngx_str_t name; 63 | } ngx_http_stat_server_t; 64 | 65 | 66 | /** Main conf */ 67 | typedef struct { 68 | 69 | ngx_uint_t enable; 70 | 71 | ngx_str_t protocol; 72 | ngx_str_t host; 73 | int port; 74 | ngx_http_stat_server_t server; 75 | 76 | ngx_shm_zone_t *shared; 77 | ngx_buf_t buffer; 78 | 79 | ngx_uint_t frequency; 80 | 81 | ngx_array_t *sources; 82 | ngx_array_t *intervals; 83 | ngx_array_t *splits; 84 | 85 | ngx_uint_t timeout; 86 | 87 | ngx_array_t *default_params; 88 | 89 | size_t shared_size; 90 | size_t buffer_size; 91 | size_t package_size; 92 | 93 | ngx_array_t *template; 94 | 95 | ngx_array_t *default_data_template; 96 | ngx_array_t *default_data_params; 97 | ngx_http_complex_value_t *default_data_filter; 98 | 99 | ngx_array_t *datas; 100 | 101 | ngx_http_stat_storage_t *storage; 102 | 103 | ngx_connection_t *connection; 104 | 105 | } ngx_http_stat_main_conf_t; 106 | 107 | /** Srv conf */ 108 | typedef struct { 109 | ngx_array_t *default_data_template; 110 | ngx_array_t *default_data_params; 111 | ngx_http_complex_value_t *default_data_filter; 112 | ngx_array_t *datas; 113 | } ngx_http_stat_srv_conf_t; 114 | 115 | /** Loc conf */ 116 | typedef struct { 117 | ngx_array_t *datas; 118 | } ngx_http_stat_loc_conf_t; 119 | 120 | /** Context */ 121 | typedef struct { 122 | ngx_int_t phase; 123 | ngx_http_stat_storage_t *storage; 124 | ngx_pool_t *pool; 125 | ngx_log_t *log; 126 | ngx_http_stat_main_conf_t *smcf; 127 | } ngx_http_stat_ctx_t; 128 | 129 | /** Acc && Inver funcs API {{{ */ 130 | typedef struct { 131 | ngx_str_t name; 132 | ngx_uint_t value; 133 | } ngx_http_stat_interval_t; 134 | 135 | typedef struct { 136 | double value; 137 | ngx_uint_t count; 138 | } ngx_http_stat_acc_t; 139 | 140 | #define P2_METRIC_COUNT 5 141 | 142 | typedef struct { 143 | double q[P2_METRIC_COUNT]; 144 | double dn[P2_METRIC_COUNT]; 145 | double np[P2_METRIC_COUNT]; 146 | ngx_int_t n[P2_METRIC_COUNT]; 147 | ngx_uint_t count; 148 | } ngx_http_stat_stt_t; 149 | 150 | typedef double (*ngx_http_stat_aggregate_pt)(ngx_http_stat_interval_t*, 151 | ngx_http_stat_acc_t*); 152 | 153 | typedef struct { 154 | ngx_str_t name; 155 | ngx_http_stat_aggregate_pt get; 156 | } ngx_http_stat_aggregate_t; 157 | 158 | 159 | double ngx_http_stat_aggregate_avg(ngx_http_stat_interval_t *interval, 160 | ngx_http_stat_acc_t *acc); 161 | double ngx_http_stat_aggregate_persec(ngx_http_stat_interval_t *interval, 162 | ngx_http_stat_acc_t *acc); 163 | double ngx_http_stat_aggregate_sum(ngx_http_stat_interval_t *interval, 164 | ngx_http_stat_acc_t *acc); 165 | void ngx_http_stat_statistic_init(ngx_http_stat_stt_t *stt, 166 | ngx_uint_t percentile); 167 | /** }}} */ 168 | 169 | /** Variables, Stats and metric API {{{ */ 170 | typedef enum { 171 | TEMPLATE_VARIABLE_HOST, 172 | TEMPLATE_VARIABLE_SPLIT, 173 | TEMPLATE_VARIABLE_PARAM, 174 | TEMPLATE_VARIABLE_INTERVAL, 175 | } ngx_http_stat_template_variable_t; 176 | 177 | typedef struct { 178 | ngx_str_t name; 179 | ngx_uint_t source; 180 | ngx_http_stat_interval_t interval; 181 | ngx_http_stat_aggregate_pt aggregate; 182 | ngx_uint_t percentile; 183 | ngx_http_stat_array_t *percentiles; 184 | } ngx_http_stat_param_t; 185 | 186 | 187 | typedef struct ngx_http_stat_metric_s { 188 | ngx_uint_t split; 189 | ngx_uint_t param; 190 | ngx_http_stat_acc_t *acc; 191 | } ngx_http_stat_metric_t; 192 | 193 | 194 | typedef struct { 195 | ngx_uint_t split; 196 | ngx_uint_t param; 197 | ngx_http_stat_stt_t *stt; 198 | } ngx_http_stat_statistic_t; 199 | 200 | 201 | typedef struct { 202 | ngx_http_stat_array_t *metrics; 203 | ngx_http_stat_array_t *statistics; 204 | ngx_http_complex_value_t *filter; 205 | } ngx_http_stat_data_t; 206 | 207 | 208 | typedef struct { 209 | ngx_str_t name; 210 | ngx_http_stat_data_t data; 211 | } ngx_http_stat_internal_t; 212 | 213 | typedef struct { 214 | ngx_str_t name; 215 | int variable; 216 | } ngx_http_stat_template_arg_t; 217 | 218 | typedef struct { 219 | ngx_str_t data; 220 | int variable; 221 | } ngx_http_stat_template_t; 222 | 223 | char *ngx_http_stat_template_compile(ngx_http_stat_ctx_t *ctx, 224 | ngx_array_t *template, ngx_http_stat_template_arg_t *args, 225 | ngx_uint_t nargs, ngx_str_t *value); 226 | u_char *ngx_http_stat_template_execute(u_char *buffer, ngx_uint_t buffer_size, 227 | ngx_array_t *template, ngx_str_t *variables[]); 228 | ngx_uint_t ngx_http_stat_template_len( ngx_array_t *template, 229 | ngx_str_t *variables[]); 230 | /** }}} */ 231 | 232 | /** Backend timed handlers {{{ */ 233 | void ngx_http_influx_udp_timer_handler(ngx_event_t *ev); 234 | /** }}} */ 235 | 236 | ngx_int_t ngx_http_stat(ngx_http_request_t *r, ngx_str_t *name, 237 | double value, char *config); 238 | void ngx_http_stat_gc(ngx_http_stat_main_conf_t *smcf, time_t ts); 239 | 240 | #endif /** NGX_HTTP_STAT_MODULE_H_INCLUDED */ 241 | 242 | -------------------------------------------------------------------------------- /t/influx_udp_test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define SERVER "127.0.0.1" 11 | #define BUFLEN 512 12 | #define PORT 8089 13 | 14 | 15 | void 16 | die(char *s) 17 | { 18 | perror(s); 19 | exit(1); 20 | } 21 | 22 | 23 | int main(void) 24 | { 25 | struct sockaddr_in si_other; 26 | int s, slen=sizeof(si_other); 27 | char message[BUFLEN]; 28 | 29 | if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 30 | die("socket"); 31 | } 32 | 33 | memset((char *) &si_other, 0, sizeof(si_other)); 34 | si_other.sin_family = AF_INET; 35 | si_other.sin_port = htons(PORT); 36 | 37 | if (inet_aton(SERVER , &si_other.sin_addr) == 0) { 38 | fprintf(stderr, "inet_aton() failed\n"); 39 | exit(1); 40 | } 41 | 42 | snprintf(message, sizeof(message), 43 | "weather,location=us-midwest-a temperature=1200 1465839850700400200\n" 44 | "weather,location=us-midwest-b temperature=120 1465839850900400200\n" 45 | ); 46 | 47 | //send the message 48 | if (sendto(s, message, strlen(message), 0, 49 | (struct sockaddr *) &si_other, slen) == -1) 50 | { 51 | die("sendto()"); 52 | } 53 | 54 | fprintf(stdout, "Message \"%s\" was sent.\n", message); 55 | 56 | close(s); 57 | 58 | return EXIT_SUCCESS; 59 | } 60 | 61 | --------------------------------------------------------------------------------