#include "mgstat.h"
#ifdef HAVE_GD
#ifdef HAVE_GD_GIF
char *file_ext = "gif";
#else
char *file_ext = "png";
#endif
#endif
#define FS ""
#define FE ""
static struct entry_t totals;
static unsigned long long max_bytes, max_hits, max_all_bytes;
extern char *out_mod; ; /* could be "", "(kB)" or "(MB)"
will be added to columns description */
char *m_name[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static char *tbl_hdr1 =
"
"
""
"| All objects ";
static char *tbl_hdr1a =
" | "
"Compressable objects ";
static char *tbl_hdr1b =
" |
"
"| " FS;
static char *tbl_hdr2=
FE " | "
"" FS "Hits " FE " | Actual bytes" FE " | "
"Bytes sent" FE " | "
"" FS "Saved bytes" FE " | \n"
"" FS "Hits" FE " | "
"Actual bytes | "
"Bytes sent | "
"" FS "Saved bytes" FE " |
\n"
;
/*
* Not used any more since program produces html output.
*/
void echo_out(struct entry_t *t)
{
unsigned long long tmp;
assert(t);
printf("Total number of request: %lld\n", t->req_c);
printf("Amount of actual bytes: %lld\n", t->all_b);
printf("Amount of bytes sent: %lld\n", t->sent_b);
printf("Number of requests that were compressed: %lld ", t->comp_c);
if(t->req_c) printf("(%lld%%)\n", t->comp_c*100/t->req_c);
printf("Without compression it would be: %lld mb (%lld kb -> %lld bytes)\n",
t->actual_b/(1024*1024), t->actual_b/1024, t->actual_b);
printf("Effective amount of bytes sent: %lld mb (%lld kb -> %lld bytes)",
t->comp_b/(1024*1024), t->comp_b/1024, t->comp_b);
if(t->actual_b) printf("\n\nOnly %lld%% of real amount of bytes was sent to the clients.",
t->comp_b*100/t->actual_b);
tmp = t->actual_b - t->comp_b;
printf("\nSaved bytes by mod_gzip: %lld mb (%lld kb -> %lld bytes)",
tmp/(1024*1024), tmp/1024, tmp);
printf("\n\n");
}
/*
* Controls data output. Depends on command line argument "-f"
* that specifies if data should be in bytes, kilobytes or megabytes.
*/
static inline long long out_fmt(unsigned long long x)
{
return x/out_ft;
}
static inline float make_prcnt(unsigned long long x, unsigned long long y)
{
return ((float)x * 100)/y;
}
static inline void echo_cell(FILE *f, unsigned long long d, int close)
{
fprintf(f, "" FS "%lld " FE , d);
if(close) fprintf(f, " | \n");
}
static void echo_cell_proc(FILE *f, unsigned long long x, unsigned long long y)
{
fprintf(f, "" FS "%lld " FE, y);
if(x) fprintf(f, " (%3.1f%%) | \n",
make_prcnt(y, x));
else fprintf(f, "\n");
}
/*
* Print one row of a table for a specified entry_t (which represents
* day or counted month summary).
* If third argument is zero then we are called from month_html()
* to generate day statistics. Otherwise we print a month summary
* (we are called from make_index()).
* Nasty.
*/
static void echo_html(struct entry_t *t, FILE *f, char *title, short year)
{
unsigned long long tmp;
assert(t);
fprintf(f, "");
if(!title) fprintf(f, "| " FS "%d" FE " | \n", t->day);
else if(!year)
fprintf(f, "" FS "%s" FE " | \n", title);
else
fprintf(f, " " FS "%s %d" FE " | \n",
title, year, title, year);
echo_cell(f, t->req_c, 1);
echo_cell(f, out_fmt(t->all_b), 1);
echo_cell_proc(f, out_fmt(t->all_b), out_fmt(t->sent_b));
/* saved bytes (all objects) */
tmp = t->all_b - t->sent_b;
fprintf(f, "" FS "%lld " FE, out_fmt(tmp));
if(t->all_b) fprintf(f," (%3.1f%%) | \n",
make_prcnt(tmp, t->all_b));
else fprintf(f, "\n");
echo_cell_proc(f, t->req_c, t->comp_c);
echo_cell(f, out_fmt(t->actual_b), 1);
/* data sent (compressable objects) */
echo_cell_proc(f, out_fmt(t->actual_b), out_fmt(t->comp_b));
/* saved bytes (compressable objects) */
tmp = t->actual_b - t->comp_b;
fprintf(f, "" FS " %lld " FE, out_fmt(tmp));
if(t->actual_b) fprintf(f," (%3.1f%%) |
\n",
make_prcnt(tmp, t->actual_b));
else fprintf(f, "\n");
}
/*
* Print table that contains mod_gzip status code statistics.
*/
static void status_codes(unsigned long long *status, unsigned long long total, FILE *f)
{
int i;
char *s;
fprintf(f, ""
"| Response Codes |
");
for(i = 0; i < MAX_STATUS_NR; i++) {
if(!status[i]) continue;
s = find_by_nr(i);
if(!s) {
fprintf(stderr, "Internal error: cannot map number %d to status.\n", i);
return;
}
fprintf(f, "| %s | %lld "
" (%3.1f%%) |
\n",
s , status[i], make_prcnt(status[i], total));
}
fprintf(f, "
\n");
}
/*
* To print a year summary we have to add status code counters
* for all months.
*/
static void tot_stat_codes(FILE *f, struct list_head *h)
{
struct list_head *x;
struct month_t *t;
int i;
unsigned long long total = 0;
unsigned long long status[MAX_STATUS_NR];
bzero(status, sizeof status);
list_for_each_r(x, h) {
t = list_entry(x, struct month_t, l_year);
for(i = 0; i < MAX_STATUS_NR; i++) {
status[i] += t->status[i];
total += t->status[i];
}
}
status_codes(status, total, f);
}
/*
* Comparision between days - for qsort()
*/
static int compar(const void *p, const void *s)
{
if((*(struct entry_t **)p)->day < (*(struct entry_t **)s)->day)
return -1;
return !((*(struct entry_t **)p)->day == (*(struct entry_t **)s)->day);
}
/*
* Returns month number for a given month name.
*/
static int month_nr(char *m)
{
int i, size = sizeof m_name/sizeof(char *);
for(i = 0; i < size; i++) {
if(!strncasecmp(m_name[i], m, 3))
return i+1;
}
errx(1, "%s: Invalid month name %s", __FUNCTION__, m);
return 0;
}
/*
* Comparision between months - for qsort()
*/
static int m_compar(const void *p, const void *s)
{
struct month_t *m1 = *(struct month_t **)p;
struct month_t *m2 = *(struct month_t **)s;
int n1, n2;
n1 = m1->year * 100 + month_nr(m1->month);
n2 = m2->year * 100 + month_nr(m2->month);
if(n1 < n2) return -1;
return !(n1 == n2);
}
/*
* Add all day counters to produce month summary.
* Also, fill totals that will be needed for a total year summary.
*/
static void calc_month(struct entry_t *t, struct list_head *head)
{
struct list_head *x;
struct entry_t *e;
list_for_each_r(x, head) {
e = list_entry(x, struct entry_t, l_month);
t->req_c += e->req_c;
t->comp_c += e->comp_c;
t->actual_b += e->actual_b;
t->comp_b += e->comp_b;
t->all_b += e->all_b;
t->sent_b += e->sent_b;
}
totals.req_c += t->req_c;
totals.comp_c += t->comp_c;
totals.actual_b += t->actual_b;
totals.comp_b += t->comp_b;
totals.all_b += t->all_b;
totals.sent_b += t->sent_b;
}
/*
* Returns number of calendar days for a given month.
*/
static int nr_days(struct month_t *m)
{
int i, size = sizeof m_name/sizeof(char *), p;
if(!strncasecmp(m->month, "Feb", 3)) return 28; // fix me!!
for(p = 0, i = 0; i < size; i++) {
if(p == 7) p = 8;
if(!strncasecmp(m_name[i], m->month, 3)) {
if(!(p%2)) return 31;
else return 30;
}
p++;
}
errx(1, "%s: internal error - unknown month %s.", __FUNCTION__, m->month);
return 0;
}
/*
* Get the maximum number of hits and bytes
*/
static void get_max(struct list_head *head)
{
struct list_head *x;
struct entry_t *e, t;
bzero(&t, sizeof t);
list_for_each_r(x, head) {
e = list_entry(x, struct entry_t, l_month);
t.req_c += e->req_c;
t.actual_b += e->actual_b;
t.all_b += e->all_b;
}
if(t.req_c > max_hits) max_hits = t.req_c;
if(t.actual_b > max_bytes) max_bytes = t.actual_b;
if(t.all_b > max_all_bytes) max_all_bytes = t.all_b;
}
/*
* Creates index.html file (and graphics charts) for a total years summary.
*/
void make_index(struct year_t *h, FILE *f)
{
struct list_head *x;
struct entry_t m_total;
struct month_t *m, **tab;
char buf[128], *tmp;
#ifdef HAVE_GD
struct gd_image *g1, *g2, *g3;
#endif
int i = 0;
if(server_name) tmp = server_name;
else if(!gethostname(buf, sizeof buf)) tmp = buf;
else tmp = "localhost";
tab = calloc(h->nr_months, sizeof m);
if(!tab) errx(1, "%s: Cannot allocate memory.", __FUNCTION__);
list_for_each_r(x, &h->months) {
m = list_entry(x, struct month_t, l_year);
if(i > h->nr_months) errx(1, "%s: Internal error.", __FUNCTION__);
tab[i] = m;
get_max(&m->days);
i++;
}
qsort(tab, h->nr_months, sizeof m, m_compar);
fprintf(f, "%s", HTML_HDR);
fprintf(f, "
Mod gzip statistics for "
"%s - year %d
\n", tmp, h->year);
tot_stat_codes(f, &h->months);
#ifdef HAVE_GD
fprintf(f, "HITS\n");
fprintf(f, "

", h->year, file_ext);
fprintf(f, "
ALL OBJECTS
");
fprintf(f, "
\n", h->year, file_ext);
fprintf(f, "
COMPRESSABLE OBJECTS
");
fprintf(f, "
\n", h->year, file_ext);
g1 = open_graph(12, CHART_BYTES, max_bytes);
g2 = open_graph(12, CHART_HITS, max_hits);
g3 = open_graph(12, CHART_BYTES, max_all_bytes);
#endif
fprintf(f, "%s%s%s%s%sMonth%s", tbl_hdr1, out_mod, tbl_hdr1a, out_mod, tbl_hdr1b, tbl_hdr2);
for(i = h->nr_months-1; i > -1 ; i--) {
int m_nr = month_nr(tab[i]->month) - 1;
bzero(&m_total, sizeof m_total);
calc_month(&m_total, &tab[i]->days);
echo_html(&m_total, f, tab[i]->month, tab[i]->year);
#ifdef HAVE_GD
draw_val(g1, m_nr, 0, m_total.actual_b);
draw_val(g1, m_nr, 1, m_total.comp_b);
draw_val(g2, m_nr, 0, m_total.req_c);
draw_val(g2, m_nr, 1, m_total.comp_c);
draw_val(g3, m_nr, 0, m_total.all_b);
draw_val(g3, m_nr, 1, m_total.sent_b);
#endif
}
echo_html(&totals, f, "Total" , 0);
#ifdef HAVE_GD
snprintf(buf, sizeof buf, "%d-bytes.%s", h->year, file_ext);
close_graph(g1, buf);
snprintf(buf, sizeof buf, "%d-hits.%s", h->year, file_ext);
close_graph(g2, buf);
snprintf(buf, sizeof buf, "%d-all-bytes.%s", h->year, file_ext);
close_graph(g3, buf);
#endif
fprintf(f, "
\n");
max_bytes = max_hits = 0;
bzero(&totals, sizeof(struct entry_t));
free(tab);
}
/*
* Creates html output file (and graphics charts) for a given month.
* Before writing to a file, days are sorted.
*/
void month_html(struct month_t *m, FILE *f)
{
struct list_head *x;
struct entry_t *t;
struct entry_t **tab;
char buf[32];
unsigned long long max, max_r, total, max_all_b;
int i = 0, p;
#ifdef HAVE_GD
struct gd_image *g1, *g2, *g3;
#endif
max_r = max = total = max_all_b = 0;
tab = calloc(m->nr_days, sizeof t);
if(!tab) errx(1, "%s: Cannot allocate memory.", __FUNCTION__);
list_for_each_r(x, &m->days) {
if(i == 31) errx(1, "%s: internal error: too many days.", __FUNCTION__);
t = list_entry(x, struct entry_t, l_month);
tab[i] = t;
i++;
total += t->req_c;
if(t->actual_b > max) max = t->actual_b;
if(t->req_c > max_r) max_r = t->req_c;
if(t->all_b > max_all_b) max_all_b = t->all_b;
}
status_codes(m->status, total, f);
qsort(tab, m->nr_days, sizeof t, compar);
#ifdef HAVE_GD
fprintf(f, "HITS
\n");
fprintf(f, "
\n",
m->month, m->year, file_ext);
fprintf(f, "
ALL OBJECTS");
fprintf(f, "

\n", m->month, m->year, file_ext);
fprintf(f, "
COMPRESSABLE OBJECTS");
fprintf(f, "

\n", m->month, m->year, file_ext);
g1 = open_graph(nr_days(m), CHART_BYTES, max);
g2 = open_graph(nr_days(m), CHART_BYTES, max_all_b);
g3 = open_graph(nr_days(m), CHART_HITS, max_r);
#endif
fprintf(f, "Daily statistics for %s %d",
m->month, m->year);
fprintf(f, "%s%s%s%s%sDay%s", tbl_hdr1, out_mod, tbl_hdr1a, out_mod, tbl_hdr1b, tbl_hdr2);
// fprintf(f, "%sDay%s", tbl_hdr1, tbl_hdr2);
for(p = 0; p < m->nr_days; p++) {
echo_html(tab[p], f, 0, 0);
#ifdef HAVE_GD
draw_val(g1, tab[p]->day-1, 0, tab[p]->actual_b);
draw_val(g1, tab[p]->day-1, 1, tab[p]->comp_b);
draw_val(g2, tab[p]->day-1, 0, tab[p]->all_b);
draw_val(g2, tab[p]->day-1, 1, tab[p]->sent_b);
draw_val(g3, tab[p]->day-1, 0, tab[p]->req_c);
draw_val(g3, tab[p]->day-1, 1, tab[p]->comp_c);
#endif
}
free(tab);
#ifdef HAVE_GD
snprintf(buf, sizeof buf, "%s%d-bytes.%s", m->month, m->year, file_ext);
close_graph(g1, buf);
snprintf(buf, sizeof buf, "%s%d-all-bytes.%s", m->month, m->year, file_ext);
close_graph(g2, buf);
snprintf(buf, sizeof buf, "%s%d-hits.%s", m->month, m->year, file_ext);
close_graph(g3, buf);
#endif
}