3410
3409
else if (s->data_source == DATA_SRC_STATS) {
3411
3410
/* dump server statistics */
3412
return produce_content_stats(s);
3415
/* unknown data source */
3416
s->txn.status = 500;
3417
client_retnclose(s, error_message(s, HTTP_ERR_500));
3418
if (!(s->flags & SN_ERR_MASK))
3419
s->flags |= SN_ERR_PRXCOND;
3420
if (!(s->flags & SN_FINST_MASK))
3421
s->flags |= SN_FINST_R;
3422
s->flags &= ~SN_SELF_GEN;
3429
* Produces statistics data for the session <s>. Expects to be called with
3430
* s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN
3431
* flag from the session, which it uses to keep on being called when there is
3432
* free space in the buffer, of simply by letting an empty buffer upon return.
3433
* It returns 1 if it changes the session state from CL_STSHUTR, otherwise 0.
3435
int produce_content_stats(struct session *s)
3437
struct buffer *rep = s->rep;
3445
switch (s->data_state) {
3447
/* the function had not been called yet */
3448
s->flags |= SN_SELF_GEN; // more data will follow
3450
chunk_printf(&msg, sizeof(trash),
3451
"HTTP/1.0 200 OK\r\n"
3452
"Cache-Control: no-cache\r\n"
3453
"Connection: close\r\n"
3454
"Content-Type: text/html\r\n"
3457
s->txn.status = 200;
3458
client_retnclose(s, &msg); // send the start of the response.
3461
if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
3462
s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
3463
if (!(s->flags & SN_FINST_MASK))
3464
s->flags |= SN_FINST_R;
3466
if (s->txn.meth == HTTP_METH_HEAD) {
3467
/* that's all we return in case of HEAD request */
3468
s->data_state = DATA_ST_FIN;
3469
s->flags &= ~SN_SELF_GEN;
3473
s->data_state = DATA_ST_HEAD; /* let's start producing data */
3477
/* WARNING! This must fit in the first buffer !!! */
3478
chunk_printf(&msg, sizeof(trash),
3479
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
3480
"<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
3481
"<style type=\"text/css\"><!--\n"
3483
" font-family: helvetica, arial;"
3485
" font-weight: normal;"
3487
" background: white;"
3490
" font-size: 0.8em;"
3494
" font-size: xx-large;"
3495
" margin-bottom: 0.5em;"
3498
" font-family: helvetica, arial;"
3499
" font-size: x-large;"
3500
" font-weight: bold;"
3501
" font-style: italic;"
3504
" margin-bottom: 0em;"
3507
" font-family: helvetica, arial;"
3509
" font-weight: bold;"
3511
" background: #e8e8d0;"
3513
" margin-bottom: 0em;"
3516
" margin-top: 0.25em;"
3517
" margin-right: 2em;"
3519
".hr {margin-top: 0.25em;"
3520
" border-color: black;"
3521
" border-bottom-style: solid;"
3523
".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
3524
".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
3525
".total {background: #20D0D0;color: #ffff80;}\n"
3526
".frontend {background: #e8e8d0;}\n"
3527
".backend {background: #e8e8d0;}\n"
3528
".active0 {background: #ff9090;}\n"
3529
".active1 {background: #ffd020;}\n"
3530
".active2 {background: #ffffa0;}\n"
3531
".active3 {background: #c0ffc0;}\n"
3532
".active4 {background: #e0e0e0;}\n"
3533
".backup0 {background: #ff9090;}\n"
3534
".backup1 {background: #ff80ff;}\n"
3535
".backup2 {background: #c060ff;}\n"
3536
".backup3 {background: #b0d0ff;}\n"
3537
".backup4 {background: #e0e0e0;}\n"
3538
"table.tbl { border-collapse: collapse; border-style: none;}\n"
3539
"table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
3540
"table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
3541
"table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
3542
"table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
3543
"table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
3544
"table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
3546
"</style></head>\n");
3548
if (buffer_write_chunk(rep, &msg) != 0)
3551
s->data_state = DATA_ST_INFO;
3555
up = (now.tv_sec - start_date.tv_sec);
3557
/* WARNING! this has to fit the first packet too.
3558
* We are around 3.5 kB, add adding entries will
3559
* become tricky if we want to support 4kB buffers !
3561
chunk_printf(&msg, sizeof(trash),
3562
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
3563
PRODUCT_NAME "</a></h1>\n"
3564
"<h2>Statistics Report for pid %d</h2>\n"
3565
"<hr width=\"100%%\" class=\"hr\">\n"
3566
"<h3>> General process information</h3>\n"
3567
"<table border=0 cols=3><tr><td align=\"left\" nowrap width=\"1%%\">\n"
3568
"<p><b>pid = </b> %d (nbproc = %d)<br>\n"
3569
"<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
3570
"<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
3571
"<b>maxsock = </b> %d<br>\n"
3572
"<b>maxconn = </b> %d (current conns = %d)<br>\n"
3573
"</td><td align=\"center\" nowrap>\n"
3574
"<table class=\"lgd\"><tr>\n"
3575
"<td class=\"active3\"> </td><td class=\"noborder\">active UP </td>"
3576
"<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
3578
"<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
3579
"<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
3581
"<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
3582
"<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
3584
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
3585
"<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
3588
"<td align=\"left\" nowrap width=\"1%%\">"
3589
"<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
3590
"<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
3591
"<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
3592
"<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
3597
pid, pid, global.nbproc,
3598
up / 86400, (up % 86400) / 3600,
3599
(up % 3600) / 60, (up % 60),
3600
global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
3601
global.rlimit_memmax ? " MB" : "",
3602
global.rlimit_nofile,
3608
if (buffer_write_chunk(rep, &msg) != 0)
3611
memset(&s->data_ctx, 0, sizeof(s->data_ctx));
3613
s->data_ctx.stats.px = proxy;
3614
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
3615
s->data_state = DATA_ST_LIST;
3620
while (s->data_ctx.stats.px) {
3621
px = s->data_ctx.stats.px;
3622
/* skip the disabled proxies and non-networked ones */
3623
if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
3624
if (produce_content_stats_proxy(s, px) == 0)
3627
s->data_ctx.stats.px = px->next;
3628
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
3630
/* here, we just have reached the last proxy */
3632
s->data_state = DATA_ST_END;
3636
chunk_printf(&msg, sizeof(trash), "</body></html>\n");
3637
if (buffer_write_chunk(rep, &msg) != 0)
3640
s->data_state = DATA_ST_FIN;
3644
s->flags &= ~SN_SELF_GEN;
3648
/* unknown state ! */
3649
s->txn.status = 500;
3650
client_retnclose(s, error_message(s, HTTP_ERR_500));
3651
if (!(s->flags & SN_ERR_MASK))
3652
s->flags |= SN_ERR_PRXCOND;
3653
if (!(s->flags & SN_FINST_MASK))
3654
s->flags |= SN_FINST_R;
3655
s->flags &= ~SN_SELF_GEN;
3662
* Dumps statistics for a proxy.
3663
* Returns 0 if it had to stop dumping data because of lack of buffer space,
3664
* ot non-zero if everything completed.
3666
int produce_content_stats_proxy(struct session *s, struct proxy *px)
3668
struct buffer *rep = s->rep;
3675
switch (s->data_ctx.stats.px_st) {
3676
case DATA_ST_PX_INIT:
3677
/* we are on a new proxy */
3679
if (s->be->uri_auth && s->be->uri_auth->scope) {
3680
/* we have a limited scope, we have to check the proxy name */
3681
struct stat_scope *scope;
3684
len = strlen(px->id);
3685
scope = s->be->uri_auth->scope;
3688
/* match exact proxy name */
3689
if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
3692
/* match '.' which means 'self' proxy */
3693
if (!strcmp(scope->px_id, ".") && px == s->fe)
3695
scope = scope->next;
3698
/* proxy name not found : don't dump anything */
3703
s->data_ctx.stats.px_st = DATA_ST_PX_TH;
3707
/* print a new table */
3708
chunk_printf(&msg, sizeof(trash),
3709
"<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
3710
"<tr align=\"center\" class=\"titre\">"
3711
"<th colspan=2 class=\"pxname\">%s</th>"
3712
"<th colspan=18 class=\"empty\"></th>"
3714
"<tr align=\"center\" class=\"titre\">"
3715
"<th rowspan=2></th>"
3716
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
3717
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
3718
"<th colspan=3>Errors</th><th colspan=6>Server</th>"
3720
"<tr align=\"center\" class=\"titre\">"
3721
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
3722
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
3723
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
3724
"<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
3725
"<th>Bck</th><th>Check</th><th>Down</th></tr>\n"
3729
if (buffer_write_chunk(rep, &msg) != 0)
3732
s->data_ctx.stats.px_st = DATA_ST_PX_FE;
3736
/* print the frontend */
3737
if (px->cap & PR_CAP_FE) {
3738
chunk_printf(&msg, sizeof(trash),
3740
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
3741
/* sessions : current, max, limit, cumul. */
3742
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
3743
/* bytes : in, out */
3744
"<td align=right>%lld</td><td align=right>%lld</td>"
3745
/* denied: req, resp */
3746
"<td align=right>%d</td><td align=right>%d</td>"
3747
/* errors : request, connect, response */
3748
"<td align=right>%d</td><td align=right></td><td align=right></td>"
3749
/* server status : reflect backend status */
3750
"<td align=center>%s</td>"
3751
/* rest of server: nothing */
3752
"<td align=center colspan=5></td></tr>"
3754
px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
3755
px->bytes_in, px->bytes_out,
3756
px->denied_req, px->denied_resp,
3758
px->state == PR_STRUN ? "OPEN" :
3759
px->state == PR_STIDLE ? "FULL" : "STOP");
3761
if (buffer_write_chunk(rep, &msg) != 0)
3765
s->data_ctx.stats.sv = px->srv; /* may be NULL */
3766
s->data_ctx.stats.px_st = DATA_ST_PX_SV;
3770
/* stats.sv has been initialized above */
3771
while (s->data_ctx.stats.sv != NULL) {
3772
static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d ↑", "UP %d/%d ↓", "UP", "<i>no check</i>" };
3773
int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4=unchecked */
3775
sv = s->data_ctx.stats.sv;
3777
/* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
3778
if (!(sv->state & SRV_CHECKED))
3780
else if (sv->state & SRV_RUNNING)
3781
if (sv->health == sv->rise + sv->fall - 1)
3782
sv_state = 3; /* UP */
3784
sv_state = 2; /* going down */
3787
sv_state = 1; /* going up */
3789
sv_state = 0; /* DOWN */
3791
chunk_printf(&msg, sizeof(trash),
3793
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
3794
/* queue : current, max */
3795
"<td align=right>%d</td><td align=right>%d</td>"
3796
/* sessions : current, max, limit, cumul */
3797
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>"
3798
/* bytes : in, out */
3799
"<td align=right>%lld</td><td align=right>%lld</td>"
3800
/* denied: req, resp */
3801
"<td align=right></td><td align=right>%d</td>"
3802
/* errors : request, connect, response */
3803
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
3805
(sv->state & SRV_BACKUP) ? "backup" : "active",
3807
sv->nbpend, sv->nbpend_max,
3808
sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess,
3809
sv->bytes_in, sv->bytes_out,
3811
sv->failed_conns, sv->failed_resp);
3814
chunk_printf(&msg, sizeof(trash), "<td nowrap>");
3815
chunk_printf(&msg, sizeof(trash),
3816
srv_hlt_st[sv_state],
3817
(sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
3818
(sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
3820
chunk_printf(&msg, sizeof(trash),
3824
"<td>%s</td><td>%s</td>"
3827
(sv->state & SRV_BACKUP) ? "-" : "Y",
3828
(sv->state & SRV_BACKUP) ? "Y" : "-");
3830
/* check failures : unique, fatal */
3831
if (sv->state & SRV_CHECKED)
3832
chunk_printf(&msg, sizeof(trash),
3833
"<td align=right>%d</td><td align=right>%d</td></tr>\n",
3834
sv->failed_checks, sv->down_trans);
3836
chunk_printf(&msg, sizeof(trash),
3837
"<td colspan=2></td></tr>\n");
3839
if (buffer_write_chunk(rep, &msg) != 0)
3842
s->data_ctx.stats.sv = sv->next;
3845
s->data_ctx.stats.px_st = DATA_ST_PX_BE;
3849
/* print the backend */
3850
if (px->cap & PR_CAP_BE) {
3851
chunk_printf(&msg, sizeof(trash),
3853
"<tr align=center class=\"backend\"><td>Backend</td>"
3854
/* queue : current, max */
3855
"<td align=right>%d</td><td align=right>%d</td>"
3856
/* sessions : current, max, limit, cumul. */
3857
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
3858
/* bytes : in, out */
3859
"<td align=right>%lld</td><td align=right>%lld</td>"
3860
/* denied: req, resp */
3861
"<td align=right>%d</td><td align=right>%d</td>"
3862
/* errors : request, connect, response */
3863
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
3864
/* server status : reflect backend status (up/down) : we display UP
3865
* if the backend has known working servers or if it has no server at
3866
* all (eg: for stats). Tthen we display the total weight, number of
3867
* active and backups. */
3868
"<td align=center>%s</td><td align=center>%d</td>"
3869
"<td align=center>%d</td><td align=center>%d</td>"
3870
/* rest of server: nothing */
3871
"<td align=center colspan=2></td></tr>"
3873
px->nbpend /* or px->totpend ? */, px->nbpend_max,
3874
px->beconn, px->beconn_max, px->fullconn, px->cum_beconn,
3875
px->bytes_in, px->bytes_out,
3876
px->denied_req, px->denied_resp,
3877
px->failed_conns, px->failed_resp,
3878
(px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN",
3879
px->srv_map_sz, px->srv_act, px->srv_bck);
3881
if (buffer_write_chunk(rep, &msg) != 0)
3885
s->data_ctx.stats.px_st = DATA_ST_PX_END;
3888
case DATA_ST_PX_END:
3889
chunk_printf(&msg, sizeof(trash), "</table><p>\n");
3891
if (buffer_write_chunk(rep, &msg) != 0)
3894
s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
3897
case DATA_ST_PX_FIN:
3901
/* unknown state, we should put an abort() here ! */
3411
int ret = stats_dump_http(s, s->be->uri_auth,
3412
(s->flags & SN_STAT_FMTCSV) ? 0 : STAT_FMT_HTML);
3415
/* -1 indicates an error */
3418
/* unknown data source or internal error */
3419
s->txn.status = 500;
3420
client_retnclose(s, error_message(s, HTTP_ERR_500));
3421
if (!(s->flags & SN_ERR_MASK))
3422
s->flags |= SN_ERR_PRXCOND;
3423
if (!(s->flags & SN_FINST_MASK))
3424
s->flags |= SN_FINST_R;
3425
s->flags &= ~SN_SELF_GEN;