You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lighttpd2/src/modules/mod_status.c

1330 lines
48 KiB
C

15 years ago
/*
* mod_status - display server status
*
* Description:
* mod_status can display a page with statistics like requests, traffic and active connections.
* It can be customized with different stylesheets (css)
15 years ago
*
* Setups:
* none
* Options:
* status.css <name|url> - set the stylesheet to use, optional
15 years ago
* type: string; values: "default", "blue" or a url to an external css file
* Actions:
* status.info - returns the status info page to the client
* status.info "short" - returns only "non-sensitive" data; no connection details, no runtime section
*
* The status page accepts parameters in the query-string:
* - mode=runtimes : show runtime information
* - format=plain : returns "short" information in plain text format, easy to parse
* - auto : returns "legacy" plain text format, for 1.5 migration or apache_ munin plugins
15 years ago
*
* Example config:
* req.path == "/srv-status" {
15 years ago
* status.css = "http://mydomain/status.css";
* status.info;
15 years ago
* }
*
* Todo:
* -
15 years ago
*
* Author:
* Copyright (c) 2008-2010 Thomas Porzelt
15 years ago
* License:
* MIT, see COPYING file in the lighttpd 2 tree
*/
#include <lighttpd/base.h>
#include <lighttpd/collect.h>
#include <lighttpd/encoding.h>
15 years ago
#include <lighttpd/plugin_core.h>
#include <lighttpd/lighttpd-glue.h>
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
LI_API gboolean mod_status_init(liModules *mods, liModule *mod);
LI_API gboolean mod_status_free(liModules *mods, liModule *mod);
15 years ago
static GString *status_info_full(liVRequest *vr, liPlugin *p, gboolean short_info, GPtrArray *result, guint uptime, liStatistics *totals, guint total_connections, guint *connection_count);
static GString *status_info_plain(liVRequest *vr, guint uptime, liStatistics *totals, guint total_connections, guint *connection_count);
static GString *status_info_auto(liVRequest *vr, guint uptime, liStatistics *totals, guint *connection_count);
static liHandlerResult status_info_runtime(liVRequest *vr, liPlugin *p);
static gint str_comp(gconstpointer a, gconstpointer b);
/* auto format constants */
static gchar liConnectionState_short[LI_CON_STATE_LAST+2] = "_cKqrhw";
15 years ago
/* html snippet constants */
static const gchar html_header[] =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
15 years ago
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
" <head>\n"
" <title>Lighttpd Status</title>\n";
static const gchar html_js[] =
" <script type=\"text/javascript\">\n"
" // <!--\n"
" var sort_dir = 'asc';\n"
" var prev_arrow = null;\n"
" var prev_elem = null;\n"
" function sort(elem, index) {\n"
" var table = elem.parentNode.parentNode.parentNode.parentNode;\n"
" var arrow = elem.parentNode.childNodes[elem.parentNode.childNodes.length - 1];\n"
" var column = elem.parentNode.cellIndex;\n"
" var rows = new Array();\n"
" for (i=1; i < table.rows.length; i++)\n"
" rows.push(table.rows[i]);\n"
" if (elem.className.indexOf('string') != -1) {\n"
" rows.sort(function (a, b) {\n"
" var text_a = a.cells[column].childNodes[index].innerHTML.toLowerCase();\n"
" var text_b = b.cells[column].childNodes[index].innerHTML.toLowerCase();\n"
" if (text_a == text_b) return 0;\n"
" return (text_a < text_b) ? -1 : 1;\n"
" });\n"
" } else {\n"
" rows.sort(function (a, b) {\n"
" var int_a = parseInt(a.cells[column].childNodes[index].getAttribute('value'));\n"
" var int_b = parseInt(b.cells[column].childNodes[index].getAttribute('value'));\n"
" return int_a - int_b;\n"
" });\n"
" }\n"
" if (prev_elem == elem) {\n"
" sort_dir = (sort_dir == 'asc') ? 'desc' : 'asc';\n"
" arrow.innerHTML = (sort_dir == 'asc') ? '&darr;' : '&uarr;';\n"
" } else {\n"
" elem.style.fontWeight = 'bold';\n"
" if (prev_elem != null) {\n"
" prev_arrow.innerHTML = '';\n"
" prev_elem.style.fontWeight = 'normal';\n"
" }\n"
" if (elem.className.indexOf('string') != -1) {\n"
" sort_dir = 'asc';\n"
" arrow.innerHTML = '&darr;';\n"
" } else {\n"
" sort_dir = 'desc';\n"
" arrow.innerHTML = '&uarr;';\n"
" }\n"
" }\n"
" if (sort_dir == 'desc')\n"
" rows.reverse();\n"
" for (i=0; i < rows.length; i++)\n"
" table.tBodies[0].appendChild(rows[i]);\n"
" prev_arrow = arrow;\n"
" prev_elem = elem;\n"
" }\n"
" // -->\n"
" </script>\n";
15 years ago
static const gchar html_top[] =
" <div class=\"header\">Lighttpd Server Status | \n"
" <span style=\"font-size: 12px;\"><a href=\"?\">main</a></strong> - <a href=\"?mode=runtime\">runtime</a></span>"
" </div>\n"
15 years ago
" <div class=\"spacer\">\n"
" <strong>Memory Usage</strong>: <span>%s</span>"
15 years ago
" <strong>Uptime</strong>: <span>%s</span>\n"
" <strong>Started at</strong>: <span>%s</span>\n"
" <strong>Version</strong>: <span>" PACKAGE_VERSION " (" __DATE__ " " __TIME__ ")</span>\n"
" </div>\n";
static const gchar html_top_short[] =
" <div class=\"header\">Lighttpd Server Status</div>\n"
" <div class=\"spacer\">\n"
" <strong>Memory Usage</strong>: <span>%s</span>"
" <strong>Uptime</strong>: <span>%s</span>\n"
" <strong>Started at</strong>: <span>%s</span>\n"
" </div>\n";
15 years ago
static const gchar html_worker_th[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\"></th>\n"
" <th style=\"width: 175px;\">Requests</th>\n"
" <th style=\"width: 175px;\">Traffic in</th>\n"
" <th style=\"width: 175px;\">Traffic out</th>\n"
" <th style=\"width: 175px;\">Active connections</th>\n"
15 years ago
" </tr>\n";
static const gchar html_worker_th_avg[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\"></th>\n"
" <th style=\"width: 175px;\">Requests / s</th>\n"
" <th style=\"width: 175px;\">Traffic in / s</th>\n"
" <th style=\"width: 175px;\">Traffic out / s</th>\n"
" <th style=\"width: 175px;\">Active connections</th>\n"
15 years ago
" </tr>\n";
static const gchar html_worker_row[] =
" <tr class=\"%s\">\n"
" <td class=\"left\">%s</td>\n"
" <td>%s (%" G_GUINT64_FORMAT "%%)</td>\n"
" <td>%s (%" G_GUINT64_FORMAT "%%)</td>\n"
" <td>%s (%" G_GUINT64_FORMAT "%%)</td>\n"
" <td>%u (%u%%)</td>\n"
" </tr>\n";
static const gchar html_worker_row_avg[] =
" <tr class=\"%s\">\n"
" <td class=\"left\">%s</td>\n"
" <td>%s</td>\n"
" <td>%s</td>\n"
" <td>%s</td>\n"
" <td>%u</td>\n"
" </tr>\n";
static const gchar html_connections_sum[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\">inactive</th>\n"
" <th style=\"width: 100px;\">request start</th>\n"
" <th style=\"width: 150px;\">read request header</th>\n"
" <th style=\"width: 150px;\">handle request</th>\n"
" <th style=\"width: 150px;\">write response</th>\n"
" <th style=\"width: 150px;\">keep-alive</th>\n"
" </tr>\n"
" <tr>\n"
" <td>%u</td>\n"
" <td>%u</td>\n"
" <td>%u</td>\n"
" <td>%u</td>\n"
" <td>%u</td>\n"
" <td>%u</td>\n"
" </tr>\n"
" </table>\n";
static const gchar html_status_codes[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\">1xx (info)</th>\n"
" <th style=\"width: 175px;\">2xx (success)</th>\n"
" <th style=\"width: 175px;\">3xx (redirect)</th>\n"
" <th style=\"width: 175px;\">4xx (client error)</th>\n"
" <th style=\"width: 175px;\">5xx (server error)</th>\n"
" </tr>\n"
" <tr>\n"
" <td>%" G_GUINT64_FORMAT "</td>\n"
" <td>%" G_GUINT64_FORMAT "</td>\n"
" <td>%" G_GUINT64_FORMAT "</td>\n"
" <td>%" G_GUINT64_FORMAT "</td>\n"
" <td>%" G_GUINT64_FORMAT "</td>\n"
" </tr>\n"
" </table>\n";
15 years ago
static const gchar html_connections_th[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th class=\"left\"><span class=\"string\" onclick=\"sort(this, 0); return false;\">Client</span><span></span></th>\n"
" <th style=\"width: 140px;\"><span class=\"string\" onclick=\"sort(this, 0); return false;\">State</span><span></span></th>\n"
" <th style=\"width: 170px;\"><span class=\"string\" onclick=\"sort(this, 0); return false;\">Host</span><span></span></th>\n"
" <th><span class=\"string\" onclick=\"sort(this, 0); return false;\">Path+Querystring</span><span></span></th>\n"
" <th><span class=\"int\" onclick=\"sort(this, 0); return false;\">Duration</span><span></span></th>\n"
" <th><span class=\"int\" onclick=\"sort(this, 0); return false;\">Timeout</span><span></span></th>\n"
" <th>Traffic <span class=\"int\" onclick=\"sort(this, 0); return false;\">in</span><span>/</span><span class=\"int\" onclick=\"sort(this, 2); return false;\">out</span><span></span></th>\n"
" <th>Traffic <span class=\"int\" onclick=\"sort(this, 0); return false;\">in</span><span>/</span><span class=\"int\" onclick=\"sort(this, 2); return false;\">out</span><span> / s</span><span></span></th>\n"
" <th><span class=\"string\" onclick=\"sort(this, 0); return false;\">Method</span><span></span></th>\n"
" <th><span class=\"int\" onclick=\"sort(this, 0); return false;\">Request Size</span><span></span></th>\n"
" <th><span class=\"int\" onclick=\"sort(this, 0); return false;\">Response Size</span><span></span></th>\n"
15 years ago
" </tr>\n";
static const gchar html_connections_row[] =
" <tr>\n"
" <td class=\"left\"><span>%s</span></td>\n"
" <td><span>%s</span></td>\n"
" <td><span>%s</span></td>\n"
" <td class=\"left\"><span>%s%s%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span><span> / </span><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span><span> / </span><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
" <td><span>%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
" <td><span value=\"%"G_GUINT64_FORMAT"\">%s</span></td>\n"
15 years ago
" </tr>\n";
static const gchar html_server_info[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <td class=\"left\" style=\"width: 125px;\">Hostname</td>\n"
" <td class=\"left\">%s</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">User</td>\n"
" <td class=\"left\">%s (%u)</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">Event handler</td>\n"
" <td class=\"left\">%s</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">File descriptor limit</td>\n"
" <td class=\"left\">%s</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">Connection limit</td>\n"
" <td class=\"left\">%u</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">Core size limit</td>\n"
" <td class=\"left\">%s</td>\n"
" </tr>\n"
" <tr>\n"
" <td class=\"left\">IO Timeout</td>\n"
" <td class=\"left\">%"G_GUINT64_FORMAT" seconds</td>\n"
" </tr>\n"
" </table>\n";
static const gchar html_libinfo_th[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\"></th>\n"
" <th style=\"width: 100px;\">linked</th>\n"
" <th style=\"width: 100px;\">headers</th>\n"
" </tr>\n";
static const gchar html_libinfo_row[] =
" <tr>\n"
" <td class=\"left\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" </tr>\n";
static const gchar html_libev_th[] =
" <table cellspacing=\"0\">\n"
" <tr>\n"
" <th style=\"width: 100px;\"></th>\n"
" <th style=\"width: 100px;\">select</th>\n"
" <th style=\"width: 100px;\">poll</th>\n"
" <th style=\"width: 100px;\">epoll</th>\n"
" <th style=\"width: 100px;\">kqueue</th>\n"
" <th style=\"width: 100px;\">/dev/poll</th>\n"
" <th style=\"width: 125px;\">solaris event port</th>\n"
" </tr>\n";
static const gchar html_libev_row[] =
" <tr>\n"
" <td class=\"left\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" <td style=\"text-align: center;\">%s</td>\n"
" </tr>\n";
15 years ago
static const gchar css_default[] =
" <style type=\"text/css\">\n"
" body { margin: 0; padding: 0; font-family: \"lucida grande\",tahoma,verdana,arial,sans-serif; font-size: 12px; }\n"
" .header { padding: 5px; background-color: #6D84B4; font-size: 16px; color: white; border: 1px solid #3B5998; font-weight: bold; }\n"
" .header a { color: #F2F2F2; }\n"
15 years ago
" .spacer { background-color: #F2F2F2; border-bottom: 1px solid #CCC; padding: 5px; }\n"
" .spacer span { padding-right: 25px; }\n"
" .title { margin-left: 6px; margin-top: 20px; margin-bottom: 5px; }\n"
" .text { margin-left: 6px; margin-bottom: 5px; }\n"
15 years ago
" table { margin-left: 5px; border: 1px solid #CCC; }\n"
" th { font-weight: normal; padding: 3px; background-color: #E0E0E0;\n"
15 years ago
" border-bottom: 1px solid #BABABA; border-right: 1px solid #BABABA; border-top: 1px solid #FEFEFE; }\n"
" td { text-align: right; padding: 3px; border-bottom: 1px solid #F0F0F0; border-right: 1px solid #F8F8F8; }\n"
" .left { text-align: left; }\n"
" .totals td { border-top: 1px solid #DDDDDD; }\n"
" </style>\n";
/* blue theme by nitrox */
static const gchar css_blue[] =
" <style type=\"text/css\">\n"
" body { margin: 0; padding: 0; font-family: \"lucida grande\",tahoma,verdana,arial,sans-serif; font-size: 12px; background-color: #6d84b4; }\n"
" .header { padding: 5px; background-color: #6D84B4; font-size: 16px; color: white; border: 1px solid #3B5998; font-weight: bold; }\n"
" .header a { color: #F2F2F2; }\n"
15 years ago
" .spacer { background-color: #F2F2F2; border-bottom: 1px solid #CCC; padding: 5px; }\n"
" .spacer span { padding-right: 25px; }\n"
" .title { margin-left: 6px; margin-top: 20px; margin-bottom: 5px; }\n"
" .text { margin-left: 6px; margin-bottom: 5px; }\n"
15 years ago
" table { margin-left: 5px; border: 1px solid #CCC; }\n"
" th { font-weight: normal; padding: 3px; background-color: #E0E0E0;\n"
15 years ago
" border-bottom: 1px solid #BABABA; border-right: 1px solid #BABABA; border-top: 1px solid #FEFEFE; }\n"
" td { text-align: right; padding: 3px; border-bottom: 1px solid #F0F0F0; border-right: 1px solid #F8F8F8; }\n"
" .left { text-align: left; }\n"
" .totals td { border-top: 1px solid #DDDDDD; }\n"
" </style>\n";
static guint64 mod_status_response_codes[] = {
0, /*1xx*/
0, /*2xx*/
0, /*3xx*/
0, /*4xx*/
0, /*5xx*/
};
15 years ago
typedef struct mod_status_param mod_status_param;
struct mod_status_param {
liPlugin *p;
gboolean short_info; /* no connection details */
};
15 years ago
typedef struct mod_status_wrk_data mod_status_wrk_data;
typedef struct mod_status_con_data mod_status_con_data;
typedef struct mod_status_job mod_status_job;
15 years ago
struct mod_status_con_data {
guint worker_ndx;
liConnectionState state;
15 years ago
GString *remote_addr_str, *local_addr_str;
gboolean is_ssl, keep_alive;
GString *host, *path, *query;
liHttpMethod method;
goffset request_size;
goffset response_size;
guint64 ts_started;
guint64 ts_timeout;
15 years ago
guint64 bytes_in;
guint64 bytes_out;
guint64 bytes_in_5s_diff;
guint64 bytes_out_5s_diff;
};
struct mod_status_wrk_data {
guint worker_ndx;
liStatistics stats;
15 years ago
GArray *connections;
guint connection_count[LI_CON_STATE_LAST+1];
15 years ago
};
struct mod_status_job {
liVRequest *vr;
gpointer *context;
liPlugin *p;
gboolean short_info;
};
15 years ago
/* the CollectFunc */
static gpointer status_collect_func(liWorker *wrk, gpointer fdata) {
mod_status_wrk_data *sd = g_slice_new0(mod_status_wrk_data);
UNUSED(fdata);
15 years ago
sd->stats = wrk->stats;
sd->worker_ndx = wrk->ndx;
/* gather connection info */
sd->connections = g_array_sized_new(FALSE, TRUE, sizeof(mod_status_con_data), wrk->connections_active);
g_array_set_size(sd->connections, wrk->connections_active);
for (guint i = 0; i < wrk->connections_active; i++) {
liConnection *c = g_array_index(wrk->connections, liConnection*, i);
15 years ago
mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, i);
cd->is_ssl = c->info.is_ssl;
cd->keep_alive = c->info.keep_alive;
cd->remote_addr_str = g_string_new_len(GSTR_LEN(c->info.remote_addr_str));
cd->local_addr_str = g_string_new_len(GSTR_LEN(c->info.local_addr_str));
15 years ago
cd->host = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.host));
cd->path = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.path));
cd->query = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.query));
cd->method = c->mainvr->request.http_method;
cd->request_size = c->mainvr->request.content_length;
cd->response_size = (NULL != c->mainvr->backend_source) ? c->mainvr->backend_source->out->bytes_out : 0;
15 years ago
cd->state = c->state;
cd->bytes_in = c->info.stats.bytes_in;
cd->bytes_out = c->info.stats.bytes_out;
cd->bytes_in_5s_diff = c->info.stats.bytes_in_5s_diff;
cd->bytes_out_5s_diff = c->info.stats.bytes_out_5s_diff;
cd->ts_started = (guint64)(li_cur_ts(wrk) - c->ts_started);
if (c->state == LI_CON_STATE_KEEP_ALIVE) {
cd->ts_timeout = (guint64)(c->keep_alive_data.timeout - li_cur_ts(wrk));
} else {
cd->ts_timeout = (guint64)(wrk->srv->io_timeout - (li_cur_ts(wrk) - c->io_timeout_elem.ts));
}
sd->connection_count[c->state]++;
15 years ago
}
return sd;
}
/* the CollectCallback */
static void status_collect_cb(gpointer cbdata, gpointer fdata, GPtrArray *result, gboolean complete) {
guint i, j;
mod_status_job *job = fdata;
liVRequest *vr = job->vr;
liPlugin *p = job->p;
gboolean short_info = job->short_info;
UNUSED(cbdata);
15 years ago
if (!complete) {
/* someone called li_collect_break, so we don't need any vrequest handling here. just free the result data */
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
for (j = 0; j < sd->connections->len; j++) {
mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j);
g_string_free(cd->remote_addr_str, TRUE);
g_string_free(cd->local_addr_str, TRUE);
g_string_free(cd->host, TRUE);
g_string_free(cd->path, TRUE);
g_string_free(cd->query, TRUE);
}
g_array_free(sd->connections, TRUE);
g_slice_free(mod_status_wrk_data, sd);
}
g_slice_free(mod_status_job, job);
return;
} else {
GString *html;
gchar *val;
guint uptime, len;
15 years ago
guint total_connections = 0;
guint connection_count[LI_CON_STATE_LAST+1] = {0};
liStatistics totals = {
15 years ago
G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0),
G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0),
G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0),
0, 0, {G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0)},
G_GUINT64_CONSTANT(0), 0, 0
15 years ago
};
/* clear context so it doesn't get cleaned up anymore */
*(job->context) = NULL;
g_slice_free(mod_status_job, job);
uptime = li_cur_ts(vr->wrk) - vr->wrk->srv->started;
if (!uptime)
uptime = 1;
15 years ago
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "finished collecting data: %s", complete ? "complete" : "not complete");
}
15 years ago
/* calculate total stats over all workers */
for (i = 0; i < result->len; i++) {
15 years ago
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
totals.bytes_out += sd->stats.bytes_out;
totals.bytes_in += sd->stats.bytes_in;
totals.requests += sd->stats.requests;
totals.actions_executed += sd->stats.actions_executed;
total_connections += sd->connections->len;
totals.requests_5s_diff += sd->stats.requests_5s_diff;
totals.bytes_in_5s_diff += sd->stats.bytes_in_5s_diff;
totals.bytes_out_5s_diff += sd->stats.bytes_out_5s_diff;
totals.active_cons_cum += sd->stats.active_cons_cum;
totals.active_cons_5s += sd->stats.active_cons_5s;
totals.peak.bytes_out += sd->stats.peak.bytes_out;
totals.peak.bytes_in += sd->stats.peak.bytes_in;
totals.peak.requests += sd->stats.peak.requests;
totals.peak.active_cons += sd->stats.peak.active_cons;
for (j = 0; j <= LI_CON_STATE_LAST; ++j) {
connection_count[j] += sd->connection_count[j];
}
15 years ago
}
if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("format"), &val, &len) && strncmp(val, "plain", len) == 0) {
/* show plain text page */
html = status_info_plain(vr, uptime, &totals, total_connections, &connection_count[0]);
} else if (li_strncase_equal(vr->request.uri.query, CONST_STR_LEN("auto"))) {
/* show auto text page */
html = status_info_auto(vr, uptime, &totals, &connection_count[0]);
} else {
/* show full html page */
html = status_info_full(vr, p, short_info, result, uptime, &totals, total_connections, &connection_count[0]);
}
li_vrequest_handle_direct(vr);
vr->response.http_status = 200;
li_chunkqueue_append_string(vr->direct_out, html);
li_vrequest_joblist_append(vr);
/* free stats */
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
for (j = 0; j < sd->connections->len; j++) {
mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j);
g_string_free(cd->remote_addr_str, TRUE);
g_string_free(cd->local_addr_str, TRUE);
g_string_free(cd->host, TRUE);
g_string_free(cd->path, TRUE);
g_string_free(cd->query, TRUE);
}
g_array_free(sd->connections, TRUE);
g_slice_free(mod_status_wrk_data, sd);
}
}
}
15 years ago
static GString *status_info_full(liVRequest *vr, liPlugin *p, gboolean short_info, GPtrArray *result, guint uptime, liStatistics *totals, guint total_connections, guint *connection_count) {
GString *html, *css, *count_req, *count_bin, *count_bout, *count_mem, *tmpstr;
gchar *val;
guint i, j, len;
gchar c;
gsize mem;
html = g_string_sized_new(8 * 1024 - 1);
count_req = g_string_sized_new(10);
count_bin = g_string_sized_new(10);
count_bout = g_string_sized_new(10);
count_mem = g_string_sized_new(10);
tmpstr = vr->wrk->tmp_str;
g_string_append_len(html, CONST_STR_LEN(html_header));
g_string_append_len(html, CONST_STR_LEN(html_js));
/* auto refresh */
if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("refresh"), &val, &len)) {
g_string_append_len(html, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
/* temp char swap */
c = val[len]; val[len] = '\0';
li_string_encode_append(val, html, LI_ENCODING_HTML);
val[len] = c;
g_string_append_len(html, CONST_STR_LEN("\">\n"));
}
/* css */
css = _OPTIONPTR(vr, p, 0).string;
if (!css || !css->len) /* default css */
g_string_append_len(html, CONST_STR_LEN(css_default));
else if (g_str_equal(css->str, "blue")) /* blue css */
g_string_append_len(html, CONST_STR_LEN(css_blue));
else /* external css */
g_string_append_printf(html, " <link rel=\"stylesheet\" rev=\"stylesheet\" href=\"%s\" media=\"screen\" />\n", css->str);
g_string_append_len(html, CONST_STR_LEN(
" </head>\n"
" <body>\n"
));
mem = li_memory_usage();
if (mem)
li_counter_format(mem, COUNTER_BYTES, count_mem);
li_counter_format((guint64)(li_cur_ts(vr->wrk) - vr->wrk->srv->started), COUNTER_TIME, tmpstr);
g_string_append_printf(html, short_info ? html_top_short : html_top,
mem ? count_mem->str : "N/A",
tmpstr->str,
vr->wrk->srv->started_str->str
);
/* worker information, absolute values */
g_string_append_len(html, CONST_STR_LEN(" <div class=\"title\"><strong>Absolute stats</strong></div>\n"));
g_string_append_len(html, CONST_STR_LEN(html_worker_th));
#define PERCENTAGE(x, y) (y ? (x * 100 / y) : 0)
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
li_counter_format(sd->stats.requests, COUNTER_UNITS, count_req);
li_counter_format(sd->stats.bytes_in, COUNTER_BYTES, count_bin);
li_counter_format(sd->stats.bytes_out, COUNTER_BYTES, count_bout);
g_string_printf(tmpstr, "Worker #%u", i+1);
g_string_append_printf(html, html_worker_row, "", tmpstr->str,
count_req->str, PERCENTAGE(sd->stats.requests, totals->requests),
count_bin->str, PERCENTAGE(sd->stats.bytes_in, totals->bytes_in),
count_bout->str, PERCENTAGE(sd->stats.bytes_out, totals->bytes_out),
sd->connections->len, PERCENTAGE(sd->connections->len, total_connections));
}
#undef PERCENTAGE
li_counter_format(totals->requests, COUNTER_UNITS, count_req);
li_counter_format(totals->bytes_in, COUNTER_BYTES, count_bin);
li_counter_format(totals->bytes_out, COUNTER_BYTES, count_bout);
g_string_append_printf(html, html_worker_row, "totals", "Total",
count_req->str, G_GUINT64_CONSTANT(100),
count_bin->str, G_GUINT64_CONSTANT(100),
count_bout->str, G_GUINT64_CONSTANT(100),
total_connections, 100);
g_string_append_len(html, CONST_STR_LEN(" </table>\n"));
/* worker information, avg values */
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>Average stats</strong> (since start)</div>\n"));
g_string_append_len(html, CONST_STR_LEN(html_worker_th_avg));
#define PERCENTAGE(x) (sd->stat ## x ? (sd->stat ## x * 100 / total ## x) : 0)
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
li_counter_format(sd->stats.requests / uptime, COUNTER_UNITS, count_req);
li_counter_format(sd->stats.bytes_in / uptime, COUNTER_BYTES, count_bin);
li_counter_format(sd->stats.bytes_out / uptime, COUNTER_BYTES, count_bout);
g_string_printf(tmpstr, "Worker #%u", i+1);
g_string_append_printf(html, html_worker_row_avg, "", tmpstr->str,
count_req->str,
count_bin->str,
count_bout->str,
(guint)(sd->stats.active_cons_cum / uptime)
15 years ago
);
}
#undef PERCENTAGE
li_counter_format(totals->requests / uptime, COUNTER_UNITS, count_req);
li_counter_format(totals->bytes_in / uptime, COUNTER_BYTES, count_bin);
li_counter_format(totals->bytes_out / uptime, COUNTER_BYTES, count_bout);
g_string_append_printf(html, html_worker_row_avg, "totals", "Total",
count_req->str,
count_bin->str,
count_bout->str,
(guint)(totals->active_cons_cum / uptime)
);
g_string_append_len(html, CONST_STR_LEN(" </table>\n"));
/* worker information, 5 seconds avg values */
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>Average stats</strong> (5 seconds)</div>\n"));
g_string_append_len(html, CONST_STR_LEN(html_worker_th_avg));
#define PERCENTAGE(x) (sd->stat ## x ? (sd->stat ## x * 100 / total ## x) : 0)
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
li_counter_format(sd->stats.requests_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_UNITS, count_req);
li_counter_format(sd->stats.bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bin);
li_counter_format(sd->stats.bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bout);
g_string_printf(tmpstr, "Worker #%u", i+1);
g_string_append_printf(html, html_worker_row_avg, "", tmpstr->str,
count_req->str,
count_bin->str,
count_bout->str,
sd->stats.active_cons_5s
);
}
#undef PERCENTAGE
li_counter_format(totals->requests_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_UNITS, count_req);
li_counter_format(totals->bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bin);
li_counter_format(totals->bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bout);
g_string_append_printf(html, html_worker_row_avg, "totals", "Total",
count_req->str,
count_bin->str,
count_bout->str,
totals->active_cons_5s
);
g_string_append_len(html, CONST_STR_LEN(" </table>\n"));
/* worker information, peak values */
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>Peak stats</strong></div>\n"));
g_string_append_len(html, CONST_STR_LEN(html_worker_th_avg));
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
li_counter_format(sd->stats.peak.requests, COUNTER_UNITS, count_req);
li_counter_format(sd->stats.peak.bytes_in, COUNTER_BYTES, count_bin);
li_counter_format(sd->stats.peak.bytes_out, COUNTER_BYTES, count_bout);
g_string_printf(tmpstr, "Worker #%u", i+1);
g_string_append_printf(html, html_worker_row_avg, "", tmpstr->str,
count_req->str,
count_bin->str,
count_bout->str,
sd->stats.peak.active_cons
);
}
li_counter_format(totals->peak.requests, COUNTER_UNITS, count_req);
li_counter_format(totals->peak.bytes_in, COUNTER_BYTES, count_bin);
li_counter_format(totals->peak.bytes_out, COUNTER_BYTES, count_bout);
g_string_append_printf(html, html_worker_row_avg, "totals", "Total",
count_req->str,
count_bin->str,
count_bout->str,
totals->peak.active_cons
);
g_string_append_len(html, CONST_STR_LEN(" </table>\n"));
/* connection counts */
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>Connections</strong> (states, sum)</div>\n"));
g_string_append_printf(html, html_connections_sum, connection_count[0] + connection_count[1],
connection_count[3], connection_count[4], connection_count[5], connection_count[6],
connection_count[2]
);
/* response status codes */
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>HTTP Status codes</strong> (sum)</div>\n"));
g_string_append_printf(html, html_status_codes, mod_status_response_codes[0], mod_status_response_codes[1],
mod_status_response_codes[2], mod_status_response_codes[3], mod_status_response_codes[4]
);
/* list connections */
if (!short_info) {
GString *ts_started, *ts_timeout, *bytes_in, *bytes_out, *bytes_in_5s, *bytes_out_5s;
GString *req_len, *resp_len;
ts_started = g_string_sized_new(15);
ts_timeout = g_string_sized_new(15);
bytes_in = g_string_sized_new(10);
bytes_out = g_string_sized_new(10);
bytes_in_5s = g_string_sized_new(10);
bytes_out_5s = g_string_sized_new(10);
req_len = g_string_sized_new(10);
resp_len = g_string_sized_new(10);
g_string_append_len(html, CONST_STR_LEN("<div class=\"title\"><strong>Active connections</strong></div>\n"));
g_string_append_len(html, CONST_STR_LEN(html_connections_th));
for (i = 0; i < result->len; i++) {
mod_status_wrk_data *sd = g_ptr_array_index(result, i);
for (j = 0; j < sd->connections->len; j++) {
mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j);
15 years ago
li_counter_format(cd->ts_started, COUNTER_TIME, ts_started);
li_counter_format(cd->ts_timeout, COUNTER_TIME, ts_timeout);
li_counter_format(cd->bytes_in, COUNTER_BYTES, bytes_in);
li_counter_format(cd->bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, bytes_in_5s);
li_counter_format(cd->bytes_out, COUNTER_BYTES, bytes_out);
li_counter_format(cd->bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, bytes_out_5s);
li_counter_format(cd->request_size, COUNTER_BYTES, req_len);
li_counter_format(cd->response_size, COUNTER_BYTES, resp_len);
g_string_append_printf(html, html_connections_row,
cd->remote_addr_str->str,
li_connection_state_str(cd->state),
cd->host->str,
cd->path->str,
cd->query->len ? "?":"",
cd->query->len ? cd->query->str : "",
cd->ts_started, ts_started->str,
cd->ts_timeout, ts_timeout->str,
cd->bytes_in, bytes_in->str,
cd->bytes_out, bytes_out->str,
cd->bytes_in_5s_diff / G_GUINT64_CONSTANT(5), bytes_in_5s->str,
cd->bytes_out_5s_diff / G_GUINT64_CONSTANT(5), bytes_out_5s->str,
(cd->state >= LI_CON_STATE_HANDLE_MAINVR) ? li_http_method_string(cd->method, &len) : "",
cd->request_size !=