├── README
├── config
├── example
├── nginx.conf
└── upstream_status.html
└── ngx_http_upstream_status_module.c
/README:
--------------------------------------------------------------------------------
1 | nginx upstream status
2 | =====================
3 |
4 | This module provides a handler called upstream_status that can be used as follows:
5 |
6 | location /foo {
7 | upstream_status;
8 | }
9 |
10 |
11 | It reports all the upstream blocks configured for this server. For upstreams managed using the round robin (default upstream), it lists all the servers configured in a block and indicates the current status (up/down)
12 |
13 |
14 | Limitation
15 | ==========
16 | nginx maintains the upstream status on a per worker basis. So this module shows the status of the upstream servers as seen by the worker that served the status page. In setups where there is just one worker, this is not a issue.
17 |
18 | (Thanks to Maxim Dounin for pointing this out)
19 |
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | ngx_addon_name=ngx_http_upstream_status_module
2 | HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_status_module"
3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_status_module.c"
4 |
--------------------------------------------------------------------------------
/example/nginx.conf:
--------------------------------------------------------------------------------
1 |
2 | #user nobody;
3 | worker_processes 1;
4 |
5 | #error_log logs/error.log;
6 | #error_log logs/error.log notice;
7 | error_log logs/error.log debug;
8 |
9 | #pid logs/nginx.pid;
10 |
11 |
12 | events {
13 | worker_connections 1024;
14 | }
15 |
16 |
17 | http {
18 |
19 | upstream ussss
20 | {
21 | server 127.0.0.1:80;
22 | }
23 |
24 | upstream w2
25 | {
26 | server 127.0.0.1:80 weight=1 max_fails=2 fail_timeout=60;
27 | server 127.0.0.1:801 weight=100 max_fails=2 fail_timeout=60;
28 | }
29 |
30 | include mime.types;
31 | default_type application/octet-stream;
32 |
33 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
34 | # '$status $body_bytes_sent "$http_referer" '
35 | # '"$http_user_agent" "$http_x_forwarded_for"';
36 |
37 | #access_log logs/access.log main;
38 |
39 | sendfile on;
40 | #tcp_nopush on;
41 |
42 | #keepalive_timeout 0;
43 | keepalive_timeout 65;
44 |
45 | #gzip on;
46 |
47 | server {
48 | listen 8081;
49 | server_name localhost;
50 |
51 | #charset koi8-r;
52 |
53 | #access_log logs/host.access.log main;
54 |
55 | location / {
56 | proxy_pass http://ussss;
57 | }
58 |
59 | location /foo {
60 | upstream_status;
61 | }
62 |
63 | location /autodocs {
64 | proxy_pass http://w2;
65 | }
66 |
67 | #error_page 404 /404.html;
68 |
69 | # redirect server error pages to the static page /50x.html
70 | #
71 | error_page 500 502 503 504 /50x.html;
72 | location = /50x.html {
73 | root html;
74 | }
75 |
76 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80
77 | #
78 | #location ~ \.php$ {
79 | # proxy_pass http://127.0.0.1;
80 | #}
81 |
82 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
83 | #
84 | #location ~ \.php$ {
85 | # root html;
86 | # fastcgi_pass 127.0.0.1:9000;
87 | # fastcgi_index index.php;
88 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
89 | # include fastcgi_params;
90 | #}
91 |
92 | # deny access to .htaccess files, if Apache's document root
93 | # concurs with nginx's one
94 | #
95 | #location ~ /\.ht {
96 | # deny all;
97 | #}
98 | }
99 |
100 |
101 | # another virtual host using mix of IP-, name-, and port-based configuration
102 | #
103 | #server {
104 | # listen 8000;
105 | # listen somename:8080;
106 | # server_name somename alias another.alias;
107 |
108 | # location / {
109 | # root html;
110 | # index index.html index.htm;
111 | # }
112 | #}
113 |
114 |
115 | # HTTPS server
116 | #
117 | #server {
118 | # listen 443;
119 | # server_name localhost;
120 |
121 | # ssl on;
122 | # ssl_certificate cert.pem;
123 | # ssl_certificate_key cert.key;
124 |
125 | # ssl_session_timeout 5m;
126 |
127 | # ssl_protocols SSLv2 SSLv3 TLSv1;
128 | # ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
129 | # ssl_prefer_server_ciphers on;
130 |
131 | # location / {
132 | # root html;
133 | # index index.html index.htm;
134 | # }
135 | #}
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/example/upstream_status.html:
--------------------------------------------------------------------------------
1 |
3 |
upstream statusw2127.0.0.1:801 | down |
127.0.0.1:80 | up |
--------------------------------------------------------------------------------
/ngx_http_upstream_status_module.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 |
6 | extern ngx_module_t ngx_http_upstream_module;
7 |
8 | static char *ngx_http_upstream_status(ngx_conf_t *cf, ngx_command_t *cmd,
9 | void *conf);
10 |
11 | static ngx_command_t ngx_http_upstream_status_commands[] = {
12 |
13 | { ngx_string("upstream_status"),
14 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
15 | ngx_http_upstream_status,
16 | 0,
17 | 0,
18 | NULL },
19 |
20 | ngx_null_command
21 | };
22 |
23 | static ngx_http_module_t ngx_http_upstream_status_module_ctx = {
24 | NULL, /* preconfiguration */
25 | NULL, /* postconfiguration */
26 |
27 | NULL, /* create main configuration */
28 | NULL, /* init main configuration */
29 |
30 | NULL, /* create server configuration */
31 | NULL, /* merge server configuration */
32 |
33 | NULL, /* create location configuration */
34 | NULL /* merge location configuration */
35 | };
36 |
37 |
38 | static char response_content_type[] = "text/html";
39 |
40 | ngx_module_t ngx_http_upstream_status_module = {
41 | NGX_MODULE_V1,
42 | &ngx_http_upstream_status_module_ctx, /* module context */
43 | ngx_http_upstream_status_commands, /* module directives */
44 | NGX_HTTP_MODULE, /* module type */
45 | NULL, /* init master */
46 | NULL, /* init module */
47 | NULL, /* init process */
48 | NULL, /* init thread */
49 | NULL, /* exit thread */
50 | NULL, /* exit process */
51 | NULL, /* exit master */
52 | NGX_MODULE_V1_PADDING
53 | };
54 |
55 | static ngx_chain_t *
56 | append_buf_to_chain(ngx_pool_t *pool, ngx_chain_t *ch, ngx_buf_t *buf)
57 | {
58 | ngx_chain_t *retval;
59 |
60 | retval = ngx_pcalloc(pool, sizeof(ngx_chain_t));
61 | if(retval != NULL)
62 | {
63 | retval->next = NULL;
64 | retval->buf = buf;
65 |
66 | retval->buf->last_buf = 1;
67 |
68 | ch->buf->last_buf = 0; /* unmark last thing in chain as last*/
69 | ch->next = retval;
70 | return retval;
71 | }
72 | return NULL;
73 | }
74 |
75 | static ngx_buf_t *
76 | str_to_buf(ngx_pool_t *pool, char *str)
77 | {
78 | ngx_buf_t *buf;
79 | u_char *u_str;
80 |
81 | u_str = (u_char*) str;
82 | buf = ngx_pcalloc(pool, sizeof(ngx_buf_t));
83 |
84 | if(buf != NULL)
85 | {
86 | buf->pos = u_str;
87 | buf->last = u_str + strlen(str);
88 | buf->memory = 1;
89 | buf->last_buf = 1;
90 |
91 | return buf;
92 | }
93 | return NULL;
94 | }
95 |
96 | static ngx_chain_t *
97 | append_str_to_chain(ngx_pool_t *pool, ngx_chain_t *ch, char *str)
98 | {
99 | ngx_buf_t *buf;
100 | buf = str_to_buf(pool, str);
101 |
102 | if(buf != NULL)
103 | {
104 | return append_buf_to_chain(pool, ch, buf);
105 | }
106 | return NULL;
107 | }
108 |
109 | size_t
110 | chain_total_len(ngx_chain_t *ch)
111 | {
112 | size_t retval = 0;
113 |
114 | while(1) {
115 | retval += ngx_buf_size(ch->buf);
116 |
117 | if(ch->buf->last_buf == 1)
118 | break;
119 |
120 | ch = ch->next;
121 | }
122 |
123 | return retval;
124 | }
125 |
126 | static void
127 | ngx_http_upstream_status_writer(ngx_http_upstream_srv_conf_t *uscfp, ngx_http_request_t *r, ngx_chain_t **out)
128 | {
129 | ngx_log_t *log;
130 | ngx_uint_t i;
131 | ngx_uint_t is_upstream_rr;
132 | ngx_uint_t up_down;
133 | ngx_http_upstream_peer_t *upstream_set;
134 | ngx_http_upstream_rr_peers_t *upstream_round_robin_set;
135 | ngx_http_upstream_rr_peer_t *upstream_round_robin_peer;
136 | ngx_buf_t *buf;
137 |
138 | log = r->connection->log;
139 |
140 | ngx_log_error(NGX_LOG_DEBUG, log, 0,
141 | "service: \"%V\"", &(uscfp->host) );
142 |
143 | buf = ngx_create_temp_buf(r->pool, 200 + 2*ngx_strlen(&(uscfp->host)) );
144 | buf->last = ngx_sprintf(buf->pos, "%V", &(uscfp->host) , &(uscfp->host) );
145 | *out = append_buf_to_chain(r->pool, *out, buf);
146 |
147 | upstream_set = &(uscfp->peer);
148 |
149 | /* Crude check to see if upstream type is round robin,
150 | else we cannot interpred upstream_set->data */
151 | is_upstream_rr = (upstream_set->init == ngx_http_upstream_init_round_robin_peer);
152 | ngx_log_error(NGX_LOG_DEBUG, log, 0,
153 | "is %V upstream: %d", &(uscfp->host), is_upstream_rr);
154 |
155 |
156 | if(is_upstream_rr) {
157 | upstream_round_robin_set = upstream_set->data;
158 | for (i = 0; i < upstream_round_robin_set->number; i++) {
159 | *out = append_str_to_chain(r->pool, *out, "");
160 |
161 | upstream_round_robin_peer = &(upstream_round_robin_set->peer[i]);
162 | up_down = upstream_round_robin_peer->fails < upstream_round_robin_peer->max_fails;
163 | ngx_log_error(NGX_LOG_DEBUG, log, 0,
164 | "reals: %V %d", &(upstream_round_robin_peer->name), up_down );
165 |
166 | buf = ngx_create_temp_buf(r->pool, 20 + ngx_strlen(&(upstream_round_robin_peer->name)) );
167 | buf->last = ngx_sprintf(buf->pos, "%V | ", &(upstream_round_robin_peer->name) );
168 | *out = append_buf_to_chain(r->pool, *out, buf);
169 |
170 | buf = ngx_create_temp_buf(r->pool, 100 );
171 | buf->last = ngx_sprintf(buf->pos, "%s | ", (up_down ? "up" : "down"), (up_down ? "up" : "down") );
172 | *out = append_buf_to_chain(r->pool, *out, buf);
173 |
174 | *out = append_str_to_chain(r->pool, *out, "
");
175 | }
176 | }
177 | *out = append_str_to_chain(r->pool, *out, "
");
178 | }
179 |
180 | static ngx_int_t
181 | ngx_http_upstream_status_handler(ngx_http_request_t *r)
182 | {
183 | ngx_uint_t i;
184 | ngx_int_t rc;
185 | ngx_chain_t out, *next;
186 | ngx_http_upstream_srv_conf_t **uscfp;
187 | ngx_http_upstream_main_conf_t *umcf;
188 | ngx_log_t *log;
189 |
190 |
191 | log = r->connection->log;
192 |
193 | out.buf = str_to_buf(r->pool, "\n");
195 | out.next = NULL;
196 | next = &out;
197 |
198 | next = append_str_to_chain(r->pool, next, "upstream status");
199 |
200 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
201 | return NGX_HTTP_NOT_ALLOWED;
202 | }
203 |
204 | rc = ngx_http_discard_request_body(r);
205 |
206 | if (rc != NGX_OK) {
207 | return rc;
208 | }
209 |
210 | umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
211 |
212 | uscfp = umcf->upstreams.elts;
213 |
214 | for (i = 0; i < umcf->upstreams.nelts; i++) {
215 | ngx_http_upstream_status_writer(uscfp[i], r, &next);
216 | }
217 |
218 | append_str_to_chain(r->pool, next, "");
219 |
220 | r->headers_out.content_type.len = sizeof(response_content_type) - 1;
221 | r->headers_out.content_type.data = (u_char *) response_content_type;
222 |
223 | r->headers_out.status = NGX_HTTP_OK;
224 | r->headers_out.content_length_n = chain_total_len(&out);
225 |
226 | ngx_log_error(NGX_LOG_DEBUG, log, 0,
227 | "ob: %d", chain_total_len(&out) );
228 |
229 | rc = ngx_http_send_header(r);
230 |
231 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
232 | return rc;
233 | }
234 | return ngx_http_output_filter(r, &out);
235 | }
236 |
237 |
238 | static char *
239 | ngx_http_upstream_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
240 | {
241 | ngx_http_core_loc_conf_t *clcf;
242 |
243 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
244 | clcf->handler = ngx_http_upstream_status_handler;
245 |
246 | return NGX_CONF_OK;
247 | }
248 |
249 |
250 | /*
251 | * vim: et ts=4 sw=4
252 | */
253 |
--------------------------------------------------------------------------------