#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 = "
" "" "" "" "" "" "" "\n" "" "" "" "\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, "\n"); } static void echo_cell_proc(FILE *f, unsigned long long x, unsigned long long y) { fprintf(f, "\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, "\n", t->day); else if(!year) fprintf(f, "\n", title); else fprintf(f, "\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, "\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, "\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, "
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 "" FS "Hits" FE "Actual bytesBytes sent" FS "Saved bytes" FE "
" FS "%lld " FE , d); if(close) fprintf(f, "" FS "%lld " FE, y); if(x) fprintf(f, " (%3.1f%%)
" FS "%d" FE "" FS "%s" FE " " FS "%s %d" FE "" FS "%lld " FE, out_fmt(tmp)); if(t->all_b) fprintf(f," (%3.1f%%) " FS " %lld " FE, out_fmt(tmp)); if(t->actual_b) fprintf(f," (%3.1f%%)
" ""); 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, "\n", s , status[i], make_prcnt(status[i], total)); } fprintf(f, "
Response Codes
%s %lld " " (%3.1f%%)

\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 }