├── 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 status
ussss
127.0.0.1:80up
w2
127.0.0.1:801down
127.0.0.1:80up
-------------------------------------------------------------------------------- /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, "", &(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, "", &(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, "", (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, "
%V
%V%s
"); 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 | --------------------------------------------------------------------------------