00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "libts.h"
00025 #undef std // FIXME: remove dependency on the STL
00026 #include "ink_config.h"
00027 #include "ink_file.h"
00028 #include "I_Layout.h"
00029 #include "I_Version.h"
00030
00031
00032 #include "LogStandalone.cc"
00033
00034 #include "LogObject.h"
00035 #include "hdrs/HTTP.h"
00036
00037 #include <math.h>
00038 #include <sys/utsname.h>
00039 #if defined(solaris)
00040 #include <sys/types.h>
00041 #include <unistd.h>
00042 #endif
00043
00044 #include <iostream>
00045 #include <fstream>
00046 #include <sstream>
00047 #include <iomanip>
00048 #include <string>
00049 #include <algorithm>
00050 #include <vector>
00051 #include <list>
00052 #include <functional>
00053 #include <fcntl.h>
00054
00055 #ifndef _XOPEN_SOURCE
00056 #define _XOPEN_SOURCE 600
00057 #endif
00058
00059 using namespace std;
00060
00061
00062 #define PROGRAM_NAME "traffic_logstats"
00063
00064 const int MAX_LOGBUFFER_SIZE = 65536;
00065 const int DEFAULT_LINE_LEN = 78;
00066 const double LOG10_1024 = 3.0102999566398116;
00067 const int MAX_ORIG_STRING = 4096;
00068
00069
00070
00071
00072 const int GET_AS_INT = 5522759;
00073 const int PUT_AS_INT = 5526864;
00074 const int HEAD_AS_INT = 1145128264;
00075 const int POST_AS_INT = 1414745936;
00076
00077 const int TEXT_AS_INT = 1954047348;
00078
00079 const int JPEG_AS_INT = 1734701162;
00080 const int JPG_AS_INT = 6778986;
00081 const int GIF_AS_INT = 6711655;
00082 const int PNG_AS_INT = 6778480;
00083 const int BMP_AS_INT = 7368034;
00084 const int CSS_AS_INT = 7566179;
00085 const int XML_AS_INT = 7105912;
00086 const int HTML_AS_INT = 1819112552;
00087 const int ZIP_AS_INT = 7367034;
00088
00089 const int JAVA_AS_INT = 1635148138;
00090 const int X_JA_AS_INT = 1634348408;
00091 const int RSSp_AS_INT = 728986482;
00092 const int PLAI_AS_INT = 1767992432;
00093 const int IMAG_AS_INT = 1734438249;
00094 const int HTTP_AS_INT = 1886680168;
00095
00096
00097 struct LastState
00098 {
00099 off_t offset;
00100 ino_t st_ino;
00101 };
00102 static LastState last_state;
00103
00104
00105
00106 struct StatsCounter
00107 {
00108 int64_t count;
00109 int64_t bytes;
00110 };
00111
00112 struct ElapsedStats
00113 {
00114 int min;
00115 int max;
00116 float avg;
00117 float stddev;
00118 };
00119
00120 struct OriginStats
00121 {
00122 const char *server;
00123 StatsCounter total;
00124
00125 struct
00126 {
00127 struct
00128 {
00129 ElapsedStats hit;
00130 ElapsedStats ims;
00131 ElapsedStats refresh;
00132 ElapsedStats other;
00133 ElapsedStats total;
00134 } hits;
00135 struct
00136 {
00137 ElapsedStats miss;
00138 ElapsedStats ims;
00139 ElapsedStats refresh;
00140 ElapsedStats other;
00141 ElapsedStats total;
00142 } misses;
00143 } elapsed;
00144
00145 struct
00146 {
00147 struct
00148 {
00149 StatsCounter hit;
00150 StatsCounter ims;
00151 StatsCounter refresh;
00152 StatsCounter other;
00153 StatsCounter total;
00154 } hits;
00155 struct
00156 {
00157 StatsCounter miss;
00158 StatsCounter ims;
00159 StatsCounter refresh;
00160 StatsCounter other;
00161 StatsCounter total;
00162 } misses;
00163 struct
00164 {
00165 StatsCounter client_abort;
00166 StatsCounter connect_fail;
00167 StatsCounter invalid_req;
00168 StatsCounter unknown;
00169 StatsCounter other;
00170 StatsCounter total;
00171 } errors;
00172 StatsCounter other;
00173 } results;
00174
00175 struct
00176 {
00177 StatsCounter c_000;
00178 StatsCounter c_100;
00179 StatsCounter c_200;
00180 StatsCounter c_201;
00181 StatsCounter c_202;
00182 StatsCounter c_203;
00183 StatsCounter c_204;
00184 StatsCounter c_205;
00185 StatsCounter c_206;
00186 StatsCounter c_2xx;
00187 StatsCounter c_300;
00188 StatsCounter c_301;
00189 StatsCounter c_302;
00190 StatsCounter c_303;
00191 StatsCounter c_304;
00192 StatsCounter c_305;
00193 StatsCounter c_307;
00194 StatsCounter c_3xx;
00195 StatsCounter c_400;
00196 StatsCounter c_401;
00197 StatsCounter c_402;
00198 StatsCounter c_403;
00199 StatsCounter c_404;
00200 StatsCounter c_405;
00201 StatsCounter c_406;
00202 StatsCounter c_407;
00203 StatsCounter c_408;
00204 StatsCounter c_409;
00205 StatsCounter c_410;
00206 StatsCounter c_411;
00207 StatsCounter c_412;
00208 StatsCounter c_413;
00209 StatsCounter c_414;
00210 StatsCounter c_415;
00211 StatsCounter c_416;
00212 StatsCounter c_417;
00213 StatsCounter c_4xx;
00214 StatsCounter c_500;
00215 StatsCounter c_501;
00216 StatsCounter c_502;
00217 StatsCounter c_503;
00218 StatsCounter c_504;
00219 StatsCounter c_505;
00220 StatsCounter c_5xx;
00221 } codes;
00222
00223 struct
00224 {
00225 StatsCounter direct;
00226 StatsCounter none;
00227 StatsCounter sibling;
00228 StatsCounter parent;
00229 StatsCounter empty;
00230 StatsCounter invalid;
00231 StatsCounter other;
00232 } hierarchies;
00233
00234 struct
00235 {
00236 StatsCounter http;
00237 StatsCounter https;
00238 StatsCounter none;
00239 StatsCounter other;
00240 } schemes;
00241
00242 struct
00243 {
00244 StatsCounter options;
00245 StatsCounter get;
00246 StatsCounter head;
00247 StatsCounter post;
00248 StatsCounter put;
00249 StatsCounter del;
00250 StatsCounter trace;
00251 StatsCounter connect;
00252 StatsCounter purge;
00253 StatsCounter none;
00254 StatsCounter other;
00255 } methods;
00256
00257 struct
00258 {
00259 struct
00260 {
00261 StatsCounter plain;
00262 StatsCounter xml;
00263 StatsCounter html;
00264 StatsCounter css;
00265 StatsCounter javascript;
00266 StatsCounter other;
00267 StatsCounter total;
00268 } text;
00269 struct
00270 {
00271 StatsCounter jpeg;
00272 StatsCounter gif;
00273 StatsCounter png;
00274 StatsCounter bmp;
00275 StatsCounter other;
00276 StatsCounter total;
00277 } image;
00278 struct
00279 {
00280 StatsCounter shockwave_flash;
00281 StatsCounter quicktime;
00282 StatsCounter javascript;
00283 StatsCounter zip;
00284 StatsCounter other;
00285 StatsCounter rss_xml;
00286 StatsCounter rss_atom;
00287 StatsCounter rss_other;
00288 StatsCounter total;
00289 } application;
00290 struct
00291 {
00292 StatsCounter wav;
00293 StatsCounter mpeg;
00294 StatsCounter other;
00295 StatsCounter total;
00296 } audio;
00297 StatsCounter none;
00298 StatsCounter other;
00299 } content;
00300 };
00301
00302 struct UrlStats
00303 {
00304 bool operator < (const UrlStats& rhs) const { return req.count > rhs.req.count; }
00305
00306 const char *url;
00307 StatsCounter req;
00308 ElapsedStats time;
00309 int64_t c_000;
00310 int64_t c_2xx;
00311 int64_t c_3xx;
00312 int64_t c_4xx;
00313 int64_t c_5xx;
00314 int64_t hits;
00315 int64_t misses;
00316 int64_t errors;
00317 };
00318
00319
00320
00321 struct eqstr
00322 {
00323 inline bool operator() (const char *s1, const char *s2) const
00324 {
00325 return 0 == strcmp(s1, s2);
00326 }
00327 };
00328
00329 struct hash_fnv32 {
00330 inline uint32_t operator()(const char* s) const
00331 {
00332 uint32_t hval = (uint32_t)0x811c9dc5;
00333
00334 if (s) {
00335 while (*s) {
00336 hval ^= (uint32_t)*s++;
00337 hval *= (uint32_t)0x01000193;
00338 }
00339 }
00340
00341 return hval;
00342 }
00343 };
00344
00345 typedef std::list<UrlStats> LruStack;
00346
00347 #if HAVE_CXX_11 && HAVE_UNORDERED_MAP && HAVE_UNORDERED_SET
00348 #include <unordered_map>
00349 #include <unordered_set>
00350 typedef std::unordered_map<const char *, OriginStats *, hash_fnv32, eqstr> OriginStorage;
00351 typedef std::unordered_set<const char *, hash_fnv32, eqstr> OriginSet;
00352 typedef std::unordered_map<const char *, LruStack::iterator, hash_fnv32, eqstr> LruHash;
00353
00354
00355 template <class T, class N>
00356 void
00357 rehash(T& container, N size) {
00358 container.rehash(size);
00359 }
00360
00361 #elif HAVE_GNU_CXX_HASH_MAP
00362 #define _BACKWARD_BACKWARD_WARNING_H // needed for gcc 4.3
00363 #include <ext/hash_map>
00364 #include <ext/hash_set>
00365 typedef __gnu_cxx::hash_map<const char *, OriginStats *, hash_fnv32, eqstr> OriginStorage;
00366 typedef __gnu_cxx::hash_set<const char *, hash_fnv32, eqstr> OriginSet;
00367 typedef __gnu_cxx::hash_map<const char *, LruStack::iterator, hash_fnv32, eqstr> LruHash;
00368
00369
00370 template <class T, class N>
00371 void
00372 rehash(T& container, N size) {
00373 container.resize(size);
00374 }
00375
00376 #undef _BACKWARD_BACKWARD_WARNING_H
00377 #else
00378 #error no supported hash container
00379 #endif
00380
00381
00382 void update_elapsed(ElapsedStats &stat, const int elapsed, const StatsCounter &counter);
00383
00384 class UrlLru
00385 {
00386
00387 public:
00388 UrlLru(int size=1000000, int show_urls=0)
00389 : _size(size)
00390 {
00391 _show_urls = size > 0 ? (show_urls >= size ? size-1 : show_urls) : show_urls;
00392 _init();
00393 _reset(false);
00394 _cur = _stack.begin();
00395 }
00396
00397 void
00398 resize(int size=0)
00399 {
00400 if (0 != size)
00401 _size = size;
00402
00403 _init();
00404 _reset(true);
00405 _cur = _stack.begin();
00406 }
00407
00408 void
00409 dump(int as_object=0)
00410 {
00411 int show = _stack.size();
00412
00413 if (_show_urls > 0 && _show_urls < show)
00414 show = _show_urls;
00415
00416 _stack.sort();
00417 for (LruStack::iterator u=_stack.begin(); NULL != u->url && --show >= 0; ++u)
00418 _dump_url(u, as_object);
00419 if (as_object)
00420 std::cout << " \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << "\"" << std::endl;
00421 else
00422 std::cout << " { \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << "\" }" << std::endl;
00423 }
00424
00425 void
00426 add_stat(const char* url, int64_t bytes, int time, int result, int http_code, int as_object=0)
00427 {
00428 LruHash::iterator h = _hash.find(url);
00429
00430 if (h != _hash.end()) {
00431 LruStack::iterator &l = h->second;
00432
00433 ++(l->req.count);
00434 l->req.bytes += bytes;
00435
00436 if ((http_code >= 600) || (http_code < 200))
00437 ++(l->c_000);
00438 else if (http_code >= 500)
00439 ++(l->c_5xx);
00440 else if (http_code >= 400)
00441 ++(l->c_4xx);
00442 else if (http_code >= 300)
00443 ++(l->c_3xx);
00444 else
00445 ++(l->c_2xx);
00446
00447 switch (result) {
00448 case SQUID_LOG_TCP_HIT:
00449 case SQUID_LOG_TCP_IMS_HIT:
00450 case SQUID_LOG_TCP_REFRESH_HIT:
00451 case SQUID_LOG_TCP_DISK_HIT:
00452 case SQUID_LOG_TCP_MEM_HIT:
00453 case SQUID_LOG_TCP_REF_FAIL_HIT:
00454 case SQUID_LOG_UDP_HIT:
00455 case SQUID_LOG_UDP_WEAK_HIT:
00456 case SQUID_LOG_UDP_HIT_OBJ:
00457 ++(l->hits);
00458 break;
00459 case SQUID_LOG_TCP_MISS:
00460 case SQUID_LOG_TCP_IMS_MISS:
00461 case SQUID_LOG_TCP_REFRESH_MISS:
00462 case SQUID_LOG_TCP_EXPIRED_MISS:
00463 case SQUID_LOG_TCP_WEBFETCH_MISS:
00464 case SQUID_LOG_UDP_MISS:
00465 ++(l->misses);
00466 break;
00467 case SQUID_LOG_ERR_CLIENT_ABORT:
00468 case SQUID_LOG_ERR_CONNECT_FAIL:
00469 case SQUID_LOG_ERR_INVALID_REQ:
00470 case SQUID_LOG_ERR_UNKNOWN:
00471 case SQUID_LOG_ERR_READ_TIMEOUT:
00472 ++(l->errors);
00473 break;
00474 }
00475
00476 update_elapsed(l->time, time, l->req);
00477
00478 if (_size > 0)
00479 _stack.splice(_stack.begin(), _stack, l);
00480 } else {
00481 const char *u = ats_strdup(url);
00482 LruStack::iterator l = _stack.end();
00483
00484 if (_size > 0) {
00485 if (_cur == l) {
00486 --l;
00487 h = _hash.find(l->url);
00488 if (h != _hash.end())
00489 _hash.erase(h);
00490 if (0 == _show_urls)
00491 _dump_url(l, as_object);
00492 } else {
00493 l = _cur++;
00494 }
00495 ats_free(const_cast<char*>(l->url));
00496 } else {
00497 l = _stack.insert(l, UrlStats());
00498 }
00499
00500
00501 l->url = u;
00502 l->req.bytes = bytes;
00503 l->req.count = 1;
00504
00505 if ((http_code >= 600) || (http_code < 200))
00506 l->c_000 = 1;
00507 else if (http_code >= 500)
00508 l->c_5xx = 1;
00509 else if (http_code >= 400)
00510 l->c_4xx = 1;
00511 else if (http_code >= 300)
00512 l->c_3xx = 1;
00513 else
00514 l->c_2xx = 1;
00515
00516 switch (result) {
00517 case SQUID_LOG_TCP_HIT:
00518 case SQUID_LOG_TCP_IMS_HIT:
00519 case SQUID_LOG_TCP_REFRESH_HIT:
00520 case SQUID_LOG_TCP_DISK_HIT:
00521 case SQUID_LOG_TCP_MEM_HIT:
00522 case SQUID_LOG_TCP_REF_FAIL_HIT:
00523 case SQUID_LOG_UDP_HIT:
00524 case SQUID_LOG_UDP_WEAK_HIT:
00525 case SQUID_LOG_UDP_HIT_OBJ:
00526 l->hits = 1;
00527 break;
00528 case SQUID_LOG_TCP_MISS:
00529 case SQUID_LOG_TCP_IMS_MISS:
00530 case SQUID_LOG_TCP_REFRESH_MISS:
00531 case SQUID_LOG_TCP_EXPIRED_MISS:
00532 case SQUID_LOG_TCP_WEBFETCH_MISS:
00533 case SQUID_LOG_UDP_MISS:
00534 l->misses = 1;
00535 break;
00536 case SQUID_LOG_ERR_CLIENT_ABORT:
00537 case SQUID_LOG_ERR_CONNECT_FAIL:
00538 case SQUID_LOG_ERR_INVALID_REQ:
00539 case SQUID_LOG_ERR_UNKNOWN:
00540 case SQUID_LOG_ERR_READ_TIMEOUT:
00541 l->errors = 1;
00542 break;
00543 }
00544
00545 l->time.min = -1;
00546 l->time.max = -1;
00547 update_elapsed(l->time, time, l->req);
00548 _hash[u] = l;
00549
00550
00551 if (_size > 0)
00552 _stack.splice(_stack.begin(), _stack, l);
00553 }
00554 }
00555
00556 private:
00557 void
00558 _init()
00559 {
00560 if (_size > 0) {
00561 _stack.resize(_size);
00562 rehash(_hash, _size);
00563 }
00564 }
00565
00566 void
00567 _reset(bool free=false)
00568 {
00569 for (LruStack::iterator l=_stack.begin(); l != _stack.end(); ++l) {
00570 if (free && l->url)
00571 ats_free(const_cast<char*>(l->url));
00572 memset(&(*l), 0, sizeof(UrlStats));
00573 }
00574 }
00575
00576 void
00577 _dump_url(LruStack::iterator &u, int as_object)
00578 {
00579 if (as_object)
00580 std::cout << " \"" << u->url << "\" : { ";
00581 else
00582 std::cout << " { \"" << u->url << "\" : { ";
00583
00584 std::cout << "\"req\" : { \"total\" : \"" << u->req.count <<
00585 "\", \"hits\" : \"" << u->hits <<
00586 "\", \"misses\" : \"" << u->misses <<
00587 "\", \"errors\" : \"" << u->errors <<
00588 "\", \"000\" : \"" << u->c_000 <<
00589 "\", \"2xx\" : \"" << u->c_2xx <<
00590 "\", \"3xx\" : \"" << u->c_3xx <<
00591 "\", \"4xx\" : \"" << u->c_4xx <<
00592 "\", \"5xx\" : \"" << u->c_5xx << "\" }, ";
00593 std:: cout << "\"bytes\" : \"" << u->req.bytes << "\", ";
00594
00595 std::cout << "\"svc_t\" : { \"min\" : \"" << u->time.min <<
00596 "\", \"max\" : \"" << u->time.max <<
00597 "\", \"avg\" : \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << u->time.avg <<
00598 "\", \"dev\" : \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << u->time.stddev;
00599
00600 if (as_object)
00601 std::cout << "\" } }," << std::endl;
00602 else
00603 std::cout << "\" } } }," << std::endl;
00604 }
00605
00606 LruHash _hash;
00607 LruStack _stack;
00608 int _size, _show_urls;
00609 LruStack::iterator _cur;
00610 };
00611
00612
00613
00614
00615 static OriginStats totals;
00616 static OriginStorage origins;
00617 static OriginSet *origin_set;
00618 static UrlLru *urls;
00619 static int parse_errors;
00620
00621
00622 struct CommandLineArgs
00623 {
00624 char log_file[1024];
00625 char origin_file[1024];
00626 char origin_list[MAX_ORIG_STRING];
00627 int max_origins;
00628 char state_tag[1024];
00629 int64_t min_hits;
00630 int max_age;
00631 int line_len;
00632 int incremental;
00633 int tail;
00634 int summary;
00635 int json;
00636 int cgi;
00637 int urls;
00638 int show_urls;
00639 int as_object;
00640 int version;
00641 int help;
00642
00643 CommandLineArgs()
00644 : max_origins(0), min_hits(0), max_age(0), line_len(DEFAULT_LINE_LEN), incremental(0),
00645 tail(0), summary(0), json(0), cgi(0), urls(0), show_urls(0), as_object(0), version(0), help(0)
00646 {
00647 log_file[0] = '\0';
00648 origin_file[0] = '\0';
00649 origin_list[0] = '\0';
00650 state_tag[0] = '\0';
00651 }
00652
00653 void parse_arguments(char** argv);
00654 };
00655
00656 static CommandLineArgs cl;
00657
00658 static ArgumentDescription argument_descriptions[] = {
00659 {"help", 'h', "Give this help", "T", &cl.help, NULL, NULL},
00660 {"log_file", 'f', "Specific logfile to parse", "S1023", cl.log_file, NULL, NULL},
00661 {"origin_list", 'o', "Only show stats for listed Origins", "S4095", cl.origin_list, NULL, NULL},
00662 {"origin_file", 'O', "File listing Origins to show", "S1023", cl.origin_file, NULL, NULL},
00663 {"max_orgins", 'M', "Max number of Origins to show", "I", &cl.max_origins, NULL, NULL},
00664 {"urls", 'u', "Produce JSON stats for URLs, argument is LRU size", "I", &cl.urls, NULL, NULL},
00665 {"show_urls", 'U', "Only show max this number of URLs", "I", &cl.show_urls, NULL, NULL},
00666 {"as_object", 'A', "Produce URL stats as a JSON object instead of array", "T", &cl.as_object, NULL, NULL},
00667 {"incremental", 'i', "Incremental log parsing", "T", &cl.incremental, NULL, NULL},
00668 {"statetag", 'S', "Name of the state file to use", "S1023", cl.state_tag, NULL, NULL},
00669 {"tail", 't', "Parse the last <sec> seconds of log", "I", &cl.tail, NULL, NULL},
00670 {"summary", 's', "Only produce the summary", "T", &cl.summary, NULL, NULL},
00671 {"json", 'j', "Produce JSON formatted output", "T", &cl.json, NULL, NULL},
00672 {"cgi", 'c', "Produce HTTP headers suitable as a CGI", "T", &cl.cgi, NULL, NULL},
00673 {"min_hits", 'm', "Minimum total hits for an Origin", "L", &cl.min_hits, NULL, NULL},
00674 {"max_age", 'a', "Max age for log entries to be considered", "I", &cl.max_age, NULL, NULL},
00675 {"line_len", 'l', "Output line length", "I", &cl.line_len, NULL, NULL},
00676 {"debug_tags", 'T', "Colon-Separated Debug Tags", "S1023", &error_tags, NULL, NULL},
00677 {"version", 'V', "Print Version Id", "T", &cl.version, NULL, NULL},
00678 };
00679
00680 static const char *USAGE_LINE =
00681 "Usage: " PROGRAM_NAME " [-f logfile] [-o origin[,...]] [-O originfile] [-m minhits] [-inshv]";
00682
00683 void
00684 CommandLineArgs::parse_arguments(char** argv)
00685 {
00686
00687 process_args(argument_descriptions, countof(argument_descriptions), argv, USAGE_LINE);
00688
00689
00690 if (strstr(argv[0], ".cgi") || cgi) {
00691 char *query;
00692
00693 json = 1;
00694 cgi = 1;
00695
00696 if (NULL != (query = getenv("QUERY_STRING"))) {
00697 char buffer[MAX_ORIG_STRING];
00698 char *tok, *sep_ptr, *val;
00699
00700 ink_strlcpy(buffer, query, sizeof(buffer));
00701 unescapifyStr(buffer);
00702
00703 for (tok = strtok_r(buffer, "&", &sep_ptr); tok != NULL;) {
00704 val = strchr(tok, '=');
00705 if (val)
00706 *(val++) = '\0';
00707 if (0 == strncmp(tok, "origin_list", 11)) {
00708 ink_strlcpy(origin_list, val, sizeof(origin_list));
00709 } else if (0 == strncmp(tok, "state_tag", 9)) {
00710 ink_strlcpy(state_tag, val, sizeof(state_tag));
00711 } else if (0 == strncmp(tok, "max_origins", 11)) {
00712 max_origins = strtol(val, NULL, 10);
00713 } else if (0 == strncmp(tok, "urls", 4)) {
00714 urls = strtol(val, NULL, 10);
00715 } else if (0 == strncmp(tok, "show_urls", 9)) {
00716 show_urls = strtol(val, NULL, 10);
00717 } else if (0 == strncmp(tok, "as_object", 9)) {
00718 as_object = strtol(val, NULL, 10);
00719 } else if (0 == strncmp(tok, "min_hits", 8)) {
00720 min_hits = strtol(val, NULL, 10);
00721 } else if (0 == strncmp(tok, "incremental", 11)) {
00722 incremental = strtol(val, NULL, 10);
00723 } else {
00724
00725 }
00726
00727 tok = strtok_r(NULL, "&", &sep_ptr);
00728 }
00729 }
00730 }
00731
00732
00733 if (version) {
00734 std::cerr << appVersionInfo.FullVersionInfoStr << std::endl;
00735 _exit(0);
00736 }
00737
00738
00739 if (help) {
00740 usage(argument_descriptions, countof(argument_descriptions), USAGE_LINE);
00741 _exit(0);
00742 }
00743 }
00744
00745
00746
00747 enum ExitLevel
00748 {
00749 EXIT_OK = 0,
00750 EXIT_WARNING = 1,
00751 EXIT_CRITICAL = 2,
00752 EXIT_UNKNOWN = 3
00753 };
00754
00755 struct ExitStatus
00756 {
00757 ExitLevel level;
00758 char notice[1024];
00759
00760 ExitStatus()
00761 : level(EXIT_OK)
00762 {
00763 memset(notice, 0, sizeof(notice));
00764 }
00765
00766 void set(ExitLevel l, const char* n=NULL) {
00767 if (l > level)
00768 level = l;
00769 if (n)
00770 ink_strlcat(notice, n, sizeof(notice));
00771 }
00772
00773 void append(const char *n) {
00774 ink_strlcat(notice, n, sizeof(notice));
00775 }
00776
00777 void append(const std::string s) {
00778 ink_strlcat(notice, s.c_str(), sizeof(notice));
00779 }
00780 };
00781
00782
00783
00784 enum ParseStates
00785 {
00786 P_STATE_ELAPSED,
00787 P_STATE_IP,
00788 P_STATE_RESULT,
00789 P_STATE_CODE,
00790 P_STATE_SIZE,
00791 P_STATE_METHOD,
00792 P_STATE_URL,
00793 P_STATE_RFC931,
00794 P_STATE_HIERARCHY,
00795 P_STATE_PEER,
00796 P_STATE_TYPE,
00797 P_STATE_END
00798 };
00799
00800
00801 enum HTTPMethod
00802 {
00803 METHOD_OPTIONS,
00804 METHOD_GET,
00805 METHOD_HEAD,
00806 METHOD_POST,
00807 METHOD_PUT,
00808 METHOD_DELETE,
00809 METHOD_TRACE,
00810 METHOD_CONNECT,
00811 METHOD_PURGE,
00812 METHOD_NONE,
00813 METHOD_OTHER
00814 };
00815
00816
00817 enum URLScheme
00818 {
00819 SCHEME_HTTP,
00820 SCHEME_HTTPS,
00821 SCHEME_NONE,
00822 SCHEME_OTHER
00823 };
00824
00825
00826
00827
00828 inline void
00829 init_elapsed(OriginStats *stats)
00830 {
00831 stats->elapsed.hits.hit.min = -1;
00832 stats->elapsed.hits.ims.min = -1;
00833 stats->elapsed.hits.refresh.min = -1;
00834 stats->elapsed.hits.other.min = -1;
00835 stats->elapsed.hits.total.min = -1;
00836 stats->elapsed.misses.miss.min = -1;
00837 stats->elapsed.misses.ims.min = -1;
00838 stats->elapsed.misses.refresh.min = -1;
00839 stats->elapsed.misses.other.min = -1;
00840 stats->elapsed.misses.total.min = -1;
00841 }
00842
00843
00844 inline void
00845 update_counter(StatsCounter& counter, int size)
00846 {
00847 counter.count++;
00848 counter.bytes += size;
00849 }
00850
00851 inline void
00852 update_elapsed(ElapsedStats &stat, const int elapsed, const StatsCounter &counter)
00853 {
00854 int newcount, oldcount;
00855 float oldavg, newavg, sum_of_squares;
00856
00857
00858 if (0 == elapsed)
00859 return;
00860 if (-1 == stat.min)
00861 stat.min = elapsed;
00862 else if (stat.min > elapsed)
00863 stat.min = elapsed;
00864
00865 if (stat.max < elapsed)
00866 stat.max = elapsed;
00867
00868
00869
00870 newcount = counter.count;
00871
00872 ink_release_assert(newcount);
00873 oldcount = counter.count - 1;
00874 oldavg = stat.avg;
00875 newavg = (oldavg * oldcount + elapsed) / newcount;
00876
00877
00878 if (oldcount != 0)
00879 sum_of_squares = (stat.stddev * stat.stddev * oldcount);
00880 else
00881 sum_of_squares = 0;
00882
00883
00884 sum_of_squares = sum_of_squares + 2 * oldavg * oldcount * (oldavg - newavg)
00885 + oldcount * (newavg * newavg - oldavg * oldavg);
00886
00887
00888 sum_of_squares = sum_of_squares + (elapsed - newavg) * (elapsed - newavg);
00889
00890 stat.stddev = sqrt(sum_of_squares / newcount);
00891 stat.avg = newavg;
00892
00893 }
00894
00895
00896
00897 inline void
00898 update_results_elapsed(OriginStats * stat, int result, int elapsed, int size)
00899 {
00900 switch (result) {
00901 case SQUID_LOG_TCP_HIT:
00902 update_counter(stat->results.hits.hit, size);
00903 update_counter(stat->results.hits.total, size);
00904 update_elapsed(stat->elapsed.hits.hit, elapsed, stat->results.hits.hit);
00905 update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00906 break;
00907 case SQUID_LOG_TCP_MISS:
00908 update_counter(stat->results.misses.miss, size);
00909 update_counter(stat->results.misses.total, size);
00910 update_elapsed(stat->elapsed.misses.miss, elapsed, stat->results.misses.miss);
00911 update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00912 break;
00913 case SQUID_LOG_TCP_IMS_HIT:
00914 update_counter(stat->results.hits.ims, size);
00915 update_counter(stat->results.hits.total, size);
00916 update_elapsed(stat->elapsed.hits.ims, elapsed, stat->results.hits.ims);
00917 update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00918 break;
00919 case SQUID_LOG_TCP_IMS_MISS:
00920 update_counter(stat->results.misses.ims, size);
00921 update_counter(stat->results.misses.total, size);
00922 update_elapsed(stat->elapsed.misses.ims, elapsed, stat->results.misses.ims);
00923 update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00924 break;
00925 case SQUID_LOG_TCP_REFRESH_HIT:
00926 update_counter(stat->results.hits.refresh, size);
00927 update_counter(stat->results.hits.total, size);
00928 update_elapsed(stat->elapsed.hits.refresh, elapsed, stat->results.hits.refresh);
00929 update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00930 break;
00931 case SQUID_LOG_TCP_REFRESH_MISS:
00932 update_counter(stat->results.misses.refresh, size);
00933 update_counter(stat->results.misses.total, size);
00934 update_elapsed(stat->elapsed.misses.refresh, elapsed, stat->results.misses.refresh);
00935 update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00936 break;
00937 case SQUID_LOG_ERR_CLIENT_ABORT:
00938 update_counter(stat->results.errors.client_abort, size);
00939 update_counter(stat->results.errors.total, size);
00940 break;
00941 case SQUID_LOG_ERR_CONNECT_FAIL:
00942 update_counter(stat->results.errors.connect_fail, size);
00943 update_counter(stat->results.errors.total, size);
00944 break;
00945 case SQUID_LOG_ERR_INVALID_REQ:
00946 update_counter(stat->results.errors.invalid_req, size);
00947 update_counter(stat->results.errors.total, size);
00948 break;
00949 case SQUID_LOG_ERR_UNKNOWN:
00950 update_counter(stat->results.errors.unknown, size);
00951 update_counter(stat->results.errors.total, size);
00952 break;
00953 case SQUID_LOG_TCP_DISK_HIT:
00954 case SQUID_LOG_TCP_MEM_HIT:
00955 case SQUID_LOG_TCP_REF_FAIL_HIT:
00956 case SQUID_LOG_UDP_HIT:
00957 case SQUID_LOG_UDP_WEAK_HIT:
00958 case SQUID_LOG_UDP_HIT_OBJ:
00959 update_counter(stat->results.hits.other, size);
00960 update_counter(stat->results.hits.total, size);
00961 update_elapsed(stat->elapsed.hits.other, elapsed, stat->results.hits.other);
00962 update_elapsed(stat->elapsed.hits.total, elapsed, stat->results.hits.total);
00963 break;
00964 case SQUID_LOG_TCP_EXPIRED_MISS:
00965 case SQUID_LOG_TCP_WEBFETCH_MISS:
00966 case SQUID_LOG_UDP_MISS:
00967 update_counter(stat->results.misses.other, size);
00968 update_counter(stat->results.misses.total, size);
00969 update_elapsed(stat->elapsed.misses.other, elapsed, stat->results.misses.other);
00970 update_elapsed(stat->elapsed.misses.total, elapsed, stat->results.misses.total);
00971 break;
00972 default:
00973 if ((result >= SQUID_LOG_ERR_READ_TIMEOUT) && (result <= SQUID_LOG_ERR_UNKNOWN)) {
00974 update_counter(stat->results.errors.other, size);
00975 update_counter(stat->results.errors.total, size);
00976 } else
00977 update_counter(stat->results.other, size);
00978 break;
00979 }
00980 }
00981
00982
00983
00984
00985 inline void
00986 update_codes(OriginStats * stat, int code, int size)
00987 {
00988 switch (code) {
00989 case 100:
00990 update_counter(stat->codes.c_100, size);
00991 break;
00992
00993
00994 case 200:
00995 update_counter(stat->codes.c_200, size);
00996 break;
00997 case 201:
00998 update_counter(stat->codes.c_201, size);
00999 break;
01000 case 202:
01001 update_counter(stat->codes.c_202, size);
01002 break;
01003 case 203:
01004 update_counter(stat->codes.c_203, size);
01005 break;
01006 case 204:
01007 update_counter(stat->codes.c_204, size);
01008 break;
01009 case 205:
01010 update_counter(stat->codes.c_205, size);
01011 break;
01012 case 206:
01013 update_counter(stat->codes.c_206, size);
01014 break;
01015
01016
01017 case 300:
01018 update_counter(stat->codes.c_300, size);
01019 break;
01020 case 301:
01021 update_counter(stat->codes.c_301, size);
01022 break;
01023 case 302:
01024 update_counter(stat->codes.c_302, size);
01025 break;
01026 case 303:
01027 update_counter(stat->codes.c_303, size);
01028 break;
01029 case 304:
01030 update_counter(stat->codes.c_304, size);
01031 break;
01032 case 305:
01033 update_counter(stat->codes.c_305, size);
01034 break;
01035 case 307:
01036 update_counter(stat->codes.c_307, size);
01037 break;
01038
01039
01040 case 400:
01041 update_counter(stat->codes.c_400, size);
01042 break;
01043 case 401:
01044 update_counter(stat->codes.c_401, size);
01045 break;
01046 case 402:
01047 update_counter(stat->codes.c_402, size);
01048 break;
01049 case 403:
01050 update_counter(stat->codes.c_403, size);
01051 break;
01052 case 404:
01053 update_counter(stat->codes.c_404, size);
01054 break;
01055 case 405:
01056 update_counter(stat->codes.c_405, size);
01057 break;
01058 case 406:
01059 update_counter(stat->codes.c_406, size);
01060 break;
01061 case 407:
01062 update_counter(stat->codes.c_407, size);
01063 break;
01064 case 408:
01065 update_counter(stat->codes.c_408, size);
01066 break;
01067 case 409:
01068 update_counter(stat->codes.c_409, size);
01069 break;
01070 case 410:
01071 update_counter(stat->codes.c_410, size);
01072 break;
01073 case 411:
01074 update_counter(stat->codes.c_411, size);
01075 break;
01076 case 412:
01077 update_counter(stat->codes.c_412, size);
01078 break;
01079 case 413:
01080 update_counter(stat->codes.c_413, size);
01081 break;
01082 case 414:
01083 update_counter(stat->codes.c_414, size);
01084 break;
01085 case 415:
01086 update_counter(stat->codes.c_415, size);
01087 break;
01088 case 416:
01089 update_counter(stat->codes.c_416, size);
01090 break;
01091 case 417:
01092 update_counter(stat->codes.c_417, size);
01093 break;
01094
01095
01096 case 500:
01097 update_counter(stat->codes.c_500, size);
01098 break;
01099 case 501:
01100 update_counter(stat->codes.c_501, size);
01101 break;
01102 case 502:
01103 update_counter(stat->codes.c_502, size);
01104 break;
01105 case 503:
01106 update_counter(stat->codes.c_503, size);
01107 break;
01108 case 504:
01109 update_counter(stat->codes.c_504, size);
01110 break;
01111 case 505:
01112 update_counter(stat->codes.c_505, size);
01113 break;
01114 default:
01115 break;
01116 }
01117
01118 if ((code >= 600) || (code < 200))
01119 update_counter(stat->codes.c_000, size);
01120 else if (code >= 500)
01121 update_counter(stat->codes.c_5xx, size);
01122 else if (code >= 400)
01123 update_counter(stat->codes.c_4xx, size);
01124 else if (code >= 300)
01125 update_counter(stat->codes.c_3xx, size);
01126 else if (code >= 200)
01127 update_counter(stat->codes.c_2xx, size);
01128 }
01129
01130
01131
01132
01133 inline void
01134 update_methods(OriginStats * stat, int method, int size)
01135 {
01136
01137 switch (method) {
01138 case METHOD_GET:
01139 update_counter(stat->methods.get, size);
01140 break;
01141
01142 case METHOD_OPTIONS:
01143 update_counter(stat->methods.options, size);
01144 break;
01145
01146 case METHOD_HEAD:
01147 update_counter(stat->methods.head, size);
01148 break;
01149
01150 case METHOD_POST:
01151 update_counter(stat->methods.post, size);
01152 break;
01153
01154 case METHOD_PUT:
01155 update_counter(stat->methods.put, size);
01156 break;
01157
01158 case METHOD_DELETE:
01159 update_counter(stat->methods.del, size);
01160 break;
01161
01162 case METHOD_TRACE:
01163 update_counter(stat->methods.trace, size);
01164 break;
01165
01166 case METHOD_CONNECT:
01167 update_counter(stat->methods.connect, size);
01168 break;
01169
01170 case METHOD_PURGE:
01171 update_counter(stat->methods.purge, size);
01172 break;
01173
01174 case METHOD_NONE:
01175 update_counter(stat->methods.none, size);
01176 break;
01177
01178 default:
01179 update_counter(stat->methods.other, size);
01180 break;
01181 }
01182 }
01183
01184
01185
01186
01187 inline void
01188 update_schemes(OriginStats * stat, int scheme, int size)
01189 {
01190 if (SCHEME_HTTP == scheme)
01191 update_counter(stat->schemes.http, size);
01192 else if (SCHEME_HTTPS == scheme)
01193 update_counter(stat->schemes.https, size);
01194 else if (SCHEME_NONE == scheme)
01195 update_counter(stat->schemes.none, size);
01196 else
01197 update_counter(stat->schemes.other, size);
01198 }
01199
01200
01201
01202
01203 int
01204 parse_log_buff(LogBufferHeader * buf_header, bool summary = false)
01205 {
01206 static LogFieldList *fieldlist = NULL;
01207
01208 LogEntryHeader *entry;
01209 LogBufferIterator buf_iter(buf_header);
01210 LogField *field;
01211 OriginStorage::iterator o_iter;
01212 ParseStates state;
01213
01214 char *read_from;
01215 char *tok;
01216 char *ptr;
01217 int tok_len;
01218 int flag = 0;
01219
01220
01221 int http_code = 0, size = 0, result = 0, hier = 0, elapsed = 0;
01222 OriginStats *o_stats;
01223 char *o_server;
01224 HTTPMethod method;
01225 URLScheme scheme;
01226
01227 if (!fieldlist) {
01228 fieldlist = new LogFieldList;
01229 ink_assert(fieldlist != NULL);
01230 bool agg = false;
01231 LogFormat::parse_symbol_string(buf_header->fmt_fieldlist(), fieldlist, &agg);
01232 }
01233
01234 while ((entry = buf_iter.next())) {
01235 read_from = (char *) entry + sizeof(LogEntryHeader);
01236
01237 if ((field = fieldlist->first()))
01238 read_from += INK_MIN_ALIGN;
01239 else
01240 break;
01241
01242 state = P_STATE_ELAPSED;
01243 o_stats = NULL;
01244 o_server = NULL;
01245 method = METHOD_OTHER;
01246 scheme = SCHEME_OTHER;
01247
01248 while ((field = fieldlist->next(field))) {
01249 switch (state) {
01250 case P_STATE_ELAPSED:
01251 state = P_STATE_IP;
01252 elapsed = *((int64_t *) (read_from));
01253 read_from += INK_MIN_ALIGN;
01254 break;
01255
01256 case P_STATE_IP:
01257 state = P_STATE_RESULT;
01258
01259 {
01260 LogFieldIp* ip = reinterpret_cast<LogFieldIp*>(read_from);
01261 int len = sizeof(LogFieldIp);
01262 if (AF_INET == ip->_family) len = sizeof(LogFieldIp4);
01263 else if (AF_INET6 == ip->_family) len = sizeof(LogFieldIp6);
01264 read_from += INK_ALIGN_DEFAULT(len);
01265 }
01266 break;
01267
01268 case P_STATE_RESULT:
01269 state = P_STATE_CODE;
01270 result = *((int64_t *) (read_from));
01271 read_from += INK_MIN_ALIGN;
01272 if ((result<32) || (result>255)) {
01273 flag = 1;
01274 state = P_STATE_END;
01275 }
01276 break;
01277
01278 case P_STATE_CODE:
01279 state = P_STATE_SIZE;
01280 http_code = *((int64_t *) (read_from));
01281 read_from += INK_MIN_ALIGN;
01282 if ((http_code<0) || (http_code>999)) {
01283 flag = 1;
01284 state = P_STATE_END;
01285 }
01286 break;
01287
01288 case P_STATE_SIZE:
01289
01290
01291 state = P_STATE_METHOD;
01292 size = *((int64_t *) (read_from));
01293 read_from += INK_MIN_ALIGN;
01294 break;
01295
01296 case P_STATE_METHOD:
01297 state = P_STATE_URL;
01298 flag = 0;
01299
01300
01301 switch (*reinterpret_cast <int*>(read_from)) {
01302 case GET_AS_INT:
01303 method = METHOD_GET;
01304 read_from += LogAccess::round_strlen(3 + 1);
01305 break;
01306 case PUT_AS_INT:
01307 method = METHOD_PUT;
01308 read_from += LogAccess::round_strlen(3 + 1);
01309 break;
01310 case HEAD_AS_INT:
01311 method = METHOD_HEAD;
01312 read_from += LogAccess::round_strlen(4 + 1);
01313 break;
01314 case POST_AS_INT:
01315 method = METHOD_POST;
01316 read_from += LogAccess::round_strlen(4 + 1);
01317 break;
01318 default:
01319 tok_len = strlen(read_from);
01320 if ((5 == tok_len) && (0 == strncmp(read_from, "PURGE", 5)))
01321 method = METHOD_PURGE;
01322 else if ((6 == tok_len) && (0 == strncmp(read_from, "DELETE", 6)))
01323 method = METHOD_DELETE;
01324 else if ((7 == tok_len) && (0 == strncmp(read_from, "OPTIONS", 7)))
01325 method = METHOD_OPTIONS;
01326 else if ((1 == tok_len) && ('-' == *read_from)) {
01327 method = METHOD_NONE;
01328 flag = 1;
01329 } else {
01330 ptr = read_from;
01331 while (*ptr && isupper(*ptr))
01332 ++ptr;
01333
01334 if (*ptr != '\0')
01335 flag = 1;
01336 }
01337 read_from += LogAccess::round_strlen(tok_len + 1);
01338 break;
01339 }
01340 break;
01341
01342 case P_STATE_URL:
01343 state = P_STATE_RFC931;
01344 if (urls)
01345 urls->add_stat(read_from, size, elapsed, result, http_code, cl.as_object);
01346
01347
01348 if (0 == flag) {
01349 tok = read_from;
01350 if (HTTP_AS_INT == *reinterpret_cast <int*>(tok)) {
01351 tok += 4;
01352 if (':' == *tok) {
01353 scheme = SCHEME_HTTP;
01354 tok += 3;
01355 tok_len = strlen(tok) + 7;
01356 } else if ('s' == *tok) {
01357 scheme = SCHEME_HTTPS;
01358 tok += 4;
01359 tok_len = strlen(tok) + 8;
01360 } else
01361 tok_len = strlen(tok) + 4;
01362 } else {
01363 if ('/' == *tok)
01364 scheme = SCHEME_NONE;
01365 tok_len = strlen(tok);
01366 }
01367 if ('/' == *tok)
01368 tok++;
01369 ptr = strchr(tok, '/');
01370 if (ptr && !summary) {
01371 *ptr = '\0';
01372
01373
01374
01375 if (origin_set->empty() || (origin_set->find(tok) != origin_set->end())) {
01376 o_iter = origins.find(tok);
01377 if (origins.end() == o_iter) {
01378 o_stats = (OriginStats *)ats_malloc(sizeof(OriginStats));
01379 memset(o_stats, 0, sizeof(OriginStats));
01380 init_elapsed(o_stats);
01381 o_server = ats_strdup(tok);
01382 if (o_stats && o_server) {
01383 o_stats->server = o_server;
01384 origins[o_server] = o_stats;
01385 }
01386 } else
01387 o_stats = o_iter->second;
01388 }
01389 }
01390 } else {
01391
01392 if ('/' == *read_from)
01393 scheme = SCHEME_NONE;
01394 tok_len = strlen(read_from);
01395 }
01396 read_from += LogAccess::round_strlen(tok_len + 1);
01397
01398
01399 update_results_elapsed(&totals, result, elapsed, size);
01400 update_codes(&totals, http_code, size);
01401 update_methods(&totals, method, size);
01402 update_schemes(&totals, scheme, size);
01403 update_counter(totals.total, size);
01404 if (o_stats != NULL) {
01405 update_results_elapsed(o_stats, result, elapsed, size);
01406 update_codes(o_stats, http_code, size);
01407 update_methods(o_stats, method, size);
01408 update_schemes(o_stats, scheme, size);
01409 update_counter(o_stats->total, size);
01410 }
01411 break;
01412
01413 case P_STATE_RFC931:
01414 state = P_STATE_HIERARCHY;
01415 if ('-' == *read_from)
01416 read_from += LogAccess::round_strlen(1 + 1);
01417 else
01418 read_from += LogAccess::strlen(read_from);
01419 break;
01420
01421 case P_STATE_HIERARCHY:
01422 state = P_STATE_PEER;
01423 hier = *((int64_t *) (read_from));
01424 switch (hier) {
01425 case SQUID_HIER_NONE:
01426 update_counter(totals.hierarchies.none, size);
01427 if (o_stats != NULL)
01428 update_counter(o_stats->hierarchies.none, size);
01429 break;
01430 case SQUID_HIER_DIRECT:
01431 update_counter(totals.hierarchies.direct, size);
01432 if (o_stats != NULL)
01433 update_counter(o_stats->hierarchies.direct, size);
01434 break;
01435 case SQUID_HIER_SIBLING_HIT:
01436 update_counter(totals.hierarchies.sibling, size);
01437 if (o_stats != NULL)
01438 update_counter(o_stats->hierarchies.sibling, size);
01439 break;
01440 case SQUID_HIER_PARENT_HIT:
01441 update_counter(totals.hierarchies.parent, size);
01442 if (o_stats != NULL)
01443 update_counter(o_stats->hierarchies.direct, size);
01444 break;
01445 case SQUID_HIER_EMPTY:
01446 update_counter(totals.hierarchies.empty, size);
01447 if (o_stats != NULL)
01448 update_counter(o_stats->hierarchies.empty, size);
01449 break;
01450 default:
01451 if ((hier >= SQUID_HIER_EMPTY) && (hier < SQUID_HIER_INVALID_ASSIGNED_CODE)) {
01452 update_counter(totals.hierarchies.other, size);
01453 if (o_stats != NULL)
01454 update_counter(o_stats->hierarchies.other, size);
01455 } else {
01456 update_counter(totals.hierarchies.invalid, size);
01457 if (o_stats != NULL)
01458 update_counter(o_stats->hierarchies.invalid, size);
01459 }
01460 break;
01461 }
01462 read_from += INK_MIN_ALIGN;
01463 break;
01464
01465 case P_STATE_PEER:
01466 state = P_STATE_TYPE;
01467 if ('-' == *read_from)
01468 read_from += LogAccess::round_strlen(1 + 1);
01469 else
01470 read_from += LogAccess::strlen(read_from);
01471 break;
01472
01473 case P_STATE_TYPE:
01474 state = P_STATE_END;
01475 if (IMAG_AS_INT == *reinterpret_cast <int*>(read_from)) {
01476 update_counter(totals.content.image.total, size);
01477 if (o_stats != NULL)
01478 update_counter(o_stats->content.image.total, size);
01479 tok = read_from + 6;
01480 switch (*reinterpret_cast <int*>(tok)) {
01481 case JPEG_AS_INT:
01482 tok_len = 10;
01483 update_counter(totals.content.image.jpeg, size);
01484 if (o_stats != NULL)
01485 update_counter(o_stats->content.image.jpeg, size);
01486 break;
01487 case JPG_AS_INT:
01488 tok_len = 9;
01489 update_counter(totals.content.image.jpeg, size);
01490 if (o_stats != NULL)
01491 update_counter(o_stats->content.image.jpeg, size);
01492 break;
01493 case GIF_AS_INT:
01494 tok_len = 9;
01495 update_counter(totals.content.image.gif, size);
01496 if (o_stats != NULL)
01497 update_counter(o_stats->content.image.gif, size);
01498 break;
01499 case PNG_AS_INT:
01500 tok_len = 9;
01501 update_counter(totals.content.image.png, size);
01502 if (o_stats != NULL)
01503 update_counter(o_stats->content.image.png, size);
01504 break;
01505 case BMP_AS_INT:
01506 tok_len = 9;
01507 update_counter(totals.content.image.bmp, size);
01508 if (o_stats != NULL)
01509 update_counter(o_stats->content.image.bmp, size);
01510 break;
01511 default:
01512 tok_len = 6 + strlen(tok);
01513 update_counter(totals.content.image.other, size);
01514 if (o_stats != NULL)
01515 update_counter(o_stats->content.image.other, size);
01516 break;
01517 }
01518 } else if (TEXT_AS_INT == *reinterpret_cast <int*>(read_from)) {
01519 tok = read_from + 5;
01520 update_counter(totals.content.text.total, size);
01521 if (o_stats != NULL)
01522 update_counter(o_stats->content.text.total, size);
01523 switch (*reinterpret_cast <int*>(tok)) {
01524 case JAVA_AS_INT:
01525
01526 tok_len = 15;
01527 update_counter(totals.content.text.javascript, size);
01528 if (o_stats != NULL)
01529 update_counter(o_stats->content.text.javascript, size);
01530 break;
01531 case CSS_AS_INT:
01532 tok_len = 8;
01533 update_counter(totals.content.text.css, size);
01534 if (o_stats != NULL)
01535 update_counter(o_stats->content.text.css, size);
01536 break;
01537 case XML_AS_INT:
01538 tok_len = 8;
01539 update_counter(totals.content.text.xml, size);
01540 if (o_stats != NULL)
01541 update_counter(o_stats->content.text.xml, size);
01542 break;
01543 case HTML_AS_INT:
01544 tok_len = 9;
01545 update_counter(totals.content.text.html, size);
01546 if (o_stats != NULL)
01547 update_counter(o_stats->content.text.html, size);
01548 break;
01549 case PLAI_AS_INT:
01550 tok_len = 10;
01551 update_counter(totals.content.text.plain, size);
01552 if (o_stats != NULL)
01553 update_counter(o_stats->content.text.plain, size);
01554 break;
01555 default:
01556 tok_len = 5 + strlen(tok);;
01557 update_counter(totals.content.text.other, size);
01558 if (o_stats != NULL)
01559 update_counter(o_stats->content.text.other, size);
01560 break;
01561 }
01562 } else if (0 == strncmp(read_from, "application", 11)) {
01563 tok = read_from + 12;
01564 update_counter(totals.content.application.total, size);
01565 if (o_stats != NULL)
01566 update_counter(o_stats->content.application.total, size);
01567 switch (*reinterpret_cast <int*>(tok)) {
01568 case ZIP_AS_INT:
01569 tok_len = 15;
01570 update_counter(totals.content.application.zip, size);
01571 if (o_stats != NULL)
01572 update_counter(o_stats->content.application.zip, size);
01573 break;
01574 case JAVA_AS_INT:
01575 update_counter(totals.content.application.javascript, size);
01576 if (o_stats != NULL)
01577 update_counter(o_stats->content.application.javascript, size);
01578 case X_JA_AS_INT:
01579 tok_len = 24;
01580 update_counter(totals.content.application.javascript, size);
01581 if (o_stats != NULL)
01582 update_counter(o_stats->content.application.javascript, size);
01583 break;
01584 case RSSp_AS_INT:
01585 if (0 == strcmp(tok+4, "xml")) {
01586 tok_len = 19;
01587 update_counter(totals.content.application.rss_xml, size);
01588 if (o_stats != NULL)
01589 update_counter(o_stats->content.application.rss_xml, size);
01590 } else if (0 == strcmp(tok+4,"atom")) {
01591 tok_len = 20;
01592 update_counter(totals.content.application.rss_atom, size);
01593 if (o_stats != NULL)
01594 update_counter(o_stats->content.application.rss_atom, size);
01595 } else {
01596 tok_len = 12 + strlen(tok);
01597 update_counter(totals.content.application.rss_other, size);
01598 if (o_stats != NULL)
01599 update_counter(o_stats->content.application.rss_other, size);
01600 }
01601 break;
01602 default:
01603 if (0 == strcmp(tok, "x-shockwave-flash")) {
01604 tok_len = 29;
01605 update_counter(totals.content.application.shockwave_flash, size);
01606 if (o_stats != NULL)
01607 update_counter(o_stats->content.application.shockwave_flash, size);
01608 } else if (0 == strcmp(tok, "x-quicktimeplayer")) {
01609 tok_len = 29;
01610 update_counter(totals.content.application.quicktime, size);
01611 if (o_stats != NULL)
01612 update_counter(o_stats->content.application.quicktime, size);
01613 } else {
01614 tok_len = 12 + strlen(tok);
01615 update_counter(totals.content.application.other, size);
01616 if (o_stats != NULL)
01617 update_counter(o_stats->content.application.other, size);
01618 }
01619 }
01620 } else if (0 == strncmp(read_from, "audio", 5)) {
01621 tok = read_from + 6;
01622 tok_len = 6 + strlen(tok);
01623 update_counter(totals.content.audio.total, size);
01624 if (o_stats != NULL)
01625 update_counter(o_stats->content.audio.total, size);
01626 if ((0 == strcmp(tok, "x-wav")) || (0 == strcmp(tok, "wav"))) {
01627 update_counter(totals.content.audio.wav, size);
01628 if (o_stats != NULL)
01629 update_counter(o_stats->content.audio.wav, size);
01630 } else if ((0 == strcmp(tok, "x-mpeg")) || (0 == strcmp(tok, "mpeg"))) {
01631 update_counter(totals.content.audio.mpeg, size);
01632 if (o_stats != NULL)
01633 update_counter(o_stats->content.audio.mpeg, size);
01634 } else {
01635 update_counter(totals.content.audio.other, size);
01636 if (o_stats != NULL)
01637 update_counter(o_stats->content.audio.other, size);
01638 }
01639 } else if ('-' == *read_from) {
01640 tok_len = 1;
01641 update_counter(totals.content.none, size);
01642 if (o_stats != NULL)
01643 update_counter(o_stats->content.none, size);
01644 } else {
01645 tok_len = strlen(read_from);
01646 update_counter(totals.content.other, size);
01647 if (o_stats != NULL)
01648 update_counter(o_stats->content.other, size);
01649 }
01650 read_from += LogAccess::round_strlen(tok_len + 1);
01651 flag = 0;
01652 break;
01653
01654 case P_STATE_END:
01655
01656 if (flag) {
01657 parse_errors++;
01658 }
01659 break;
01660 }
01661 }
01662 }
01663
01664 return 0;
01665 }
01666
01667
01668
01669
01670
01671 int
01672 process_file(int in_fd, off_t offset, unsigned max_age)
01673 {
01674 char buffer[MAX_LOGBUFFER_SIZE];
01675 int nread, buffer_bytes;
01676
01677 Debug("logstats", "Processing file [offset=%" PRId64 "].", (int64_t)offset);
01678 while (true) {
01679 Debug("logstats", "Reading initial header.");
01680 buffer[0] = '\0';
01681
01682 unsigned first_read_size = sizeof(uint32_t) + sizeof(uint32_t);
01683 LogBufferHeader *header = (LogBufferHeader *)&buffer[0];
01684
01685
01686
01687
01688 if (offset > 0) {
01689 Debug("logstats", "Re-aligning file read.");
01690 while (true) {
01691 if (lseek(in_fd, offset, SEEK_SET) < 0) {
01692 Debug("logstats", "Internal seek failed (offset=%" PRId64 ").", (int64_t)offset);
01693 return 1;
01694 }
01695
01696
01697
01698 nread = read(in_fd, buffer, first_read_size);
01699 if (!nread || EOF == nread) {
01700 return 0;
01701 }
01702
01703 if (header->cookie && (LOG_SEGMENT_COOKIE == header->cookie)) {
01704 offset = 0;
01705 break;
01706 }
01707 offset++;
01708 }
01709 if (!header->cookie) {
01710 return 0;
01711 }
01712 } else {
01713 nread = read(in_fd, buffer, first_read_size);
01714 if (!nread || EOF == nread || !header->cookie)
01715 return 0;
01716
01717
01718 if (header->cookie != LOG_SEGMENT_COOKIE) {
01719 Debug("logstats", "Invalid segment cookie (expected %d, got %d)", LOG_SEGMENT_COOKIE, header->cookie);
01720 return 1;
01721 }
01722 }
01723
01724 Debug("logstats", "LogBuffer version %d, current = %d", header->version, LOG_SEGMENT_VERSION);
01725 if (header->version != LOG_SEGMENT_VERSION)
01726 return 1;
01727
01728
01729 unsigned second_read_size = sizeof(LogBufferHeader) - first_read_size;
01730 nread = read(in_fd, &buffer[first_read_size], second_read_size);
01731 if (!nread || EOF == nread) {
01732 Debug("logstats", "Second read of header failed (attemped %d bytes at offset %d, got nothing), errno=%d.", second_read_size, first_read_size, errno);
01733 return 1;
01734 }
01735
01736
01737 if (header->byte_count > sizeof(buffer)) {
01738 Debug("logstats", "Header byte count [%d] > expected [%zu]", header->byte_count, sizeof(buffer));
01739 return 1;
01740 }
01741
01742 buffer_bytes = header->byte_count - sizeof(LogBufferHeader);
01743 if (buffer_bytes <= 0 || (unsigned int) buffer_bytes > (sizeof(buffer) - sizeof(LogBufferHeader))) {
01744 Debug("logstats", "Buffer payload [%d] is wrong.", buffer_bytes);
01745 return 1;
01746 }
01747
01748 const int MAX_READ_TRIES = 5;
01749 int total_read = 0;
01750 int read_tries_remaining = MAX_READ_TRIES;
01751 do {
01752 nread = read(in_fd, &buffer[sizeof(LogBufferHeader) + total_read], buffer_bytes - total_read);
01753 if (EOF == nread || !nread) {
01754 Debug("logstats", "Read failed while reading log buffer, wanted %d bytes, nread=%d, errno=%d", buffer_bytes - total_read, nread, errno);
01755 return 1;
01756 } else {
01757 total_read += nread;
01758 }
01759
01760 if (total_read < buffer_bytes) {
01761 if (--read_tries_remaining <= 0) {
01762 Debug("logstats_failed_retries", "Unable to read after %d tries, total_read=%d, buffer_bytes=%d", MAX_READ_TRIES, total_read, buffer_bytes);
01763 return 1;
01764 }
01765
01766 Debug("logstats_partial_read", "Failed to read buffer payload [%d bytes], total_read=%d, buffer_bytes=%d, tries_remaining=%d",
01767 buffer_bytes - total_read, total_read, buffer_bytes, read_tries_remaining);
01768 usleep(50*1000);
01769 }
01770 } while (total_read < buffer_bytes);
01771
01772
01773 if (header->high_timestamp >= max_age) {
01774 if (parse_log_buff(header, cl.summary != 0) != 0) {
01775 Debug("logstats", "Failed to parse log buffer.");
01776 return 1;
01777 }
01778 } else {
01779 Debug("logstats", "Skipping old buffer (age=%d, max=%d)", header->high_timestamp, max_age);
01780 }
01781 }
01782
01783 return 0;
01784 }
01785
01786
01787
01788
01789
01790 inline int
01791 use_origin(const OriginStats * stat)
01792 {
01793 return ((stat->total.count > cl.min_hits) && (NULL != strchr(stat->server, '.')) && (NULL == strchr(stat->server, '%')));
01794 }
01795
01796
01797
01798
01799 inline void
01800 format_center(const char *str)
01801 {
01802 std::cout << std::setfill(' ') << std::setw((cl.line_len - strlen(str)) / 2 + strlen(str)) << str << std::endl << std::endl;
01803 }
01804
01805 inline void
01806 format_int(int64_t num)
01807 {
01808 if (num > 0) {
01809 int64_t mult = (int64_t) pow((double)10, (int) (log10((double)num) / 3) * 3);
01810 int64_t div;
01811 std::stringstream ss;
01812
01813 ss.fill('0');
01814 while (mult > 0) {
01815 div = num / mult;
01816 ss << div << std::setw(3);
01817 num -= (div * mult);
01818 if (mult /= 1000)
01819 ss << std::setw(0) << ',' << std::setw(3);
01820 }
01821 std::cout << ss.str();
01822 } else
01823 std::cout << '0';
01824 }
01825
01826 void
01827 format_elapsed_header()
01828 {
01829 std::cout << std::left << std::setw(24) << "Elapsed time stats";
01830 std::cout << std::right << std::setw(7) << "Min" << std::setw(13) << "Max";
01831 std::cout << std::right << std::setw(17) << "Avg" << std::setw(17) << "Std Deviation" << std::endl;
01832 std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
01833 }
01834
01835 inline void
01836 format_elapsed_line(const char *desc, const ElapsedStats &stat, bool json=false)
01837 {
01838 if (json) {
01839 std::cout << " " << '"' << desc << "\" : " << "{ ";
01840 std::cout << "\"min\": \"" << stat.min << "\", ";
01841 std::cout << "\"max\": \"" << stat.max << "\", ";
01842 std::cout << "\"avg\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.avg << "\", ";
01843 std::cout << "\"dev\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.stddev << "\" },";
01844 std::cout << std::endl;
01845 } else {
01846 std::cout << std::left << std::setw(24) << desc;
01847 std::cout << std::right << std::setw(7);
01848 format_int(stat.min);
01849 std::cout << std::right << std::setw(13);
01850 format_int(stat.max);
01851
01852 std::cout << std::right << std::setw(17) << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.avg;
01853 std::cout << std::right << std::setw(17) << std::setiosflags(ios::fixed) << std::setprecision(2) << stat.stddev;
01854 std::cout << std::endl;
01855 }
01856 }
01857
01858
01859 void
01860 format_detail_header(const char *desc)
01861 {
01862 std::cout << std::left << std::setw(29) << desc;
01863 std::cout << std::right << std::setw(15) << "Count" << std::setw(11) << "Percent";
01864 std::cout << std::right << std::setw(12) << "Bytes" << std::setw(11) << "Percent" << std::endl;
01865 std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
01866 }
01867
01868 inline void
01869 format_line(const char *desc, const StatsCounter &stat, const StatsCounter &total, bool json=false)
01870 {
01871 static char metrics[] = "KKMGTP";
01872 static char buf[64];
01873 int ix = (stat.bytes > 1024 ? (int) (log10((double)stat.bytes) / LOG10_1024) : 1);
01874
01875 if (json) {
01876 std::cout << " " << '"' << desc << "\" : " << "{ ";
01877 std::cout << "\"req\": \"" << stat.count << "\", ";
01878 std::cout << "\"req_pct\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) <<
01879 (double)stat.count / total.count * 100 << "\", ";
01880 std::cout << "\"bytes\": \"" << stat.bytes << "\", ";
01881 std::cout << "\"bytes_pct\": \"" << std::setiosflags(ios::fixed) << std::setprecision(2) <<
01882 (double)stat.bytes / total.bytes * 100 << "\" }," << std::endl;
01883 } else {
01884 std::cout << std::left << std::setw(29) << desc;
01885
01886 std::cout << std::right << std::setw(15);
01887 format_int(stat.count);
01888
01889 snprintf(buf, sizeof(buf), "%10.2f%%", ((double) stat.count / total.count * 100));
01890 std::cout << std::right << buf;
01891
01892 snprintf(buf, sizeof(buf), "%10.2f%cB", stat.bytes / pow((double)1024, ix), metrics[ix]);
01893 std::cout << std::right << buf;
01894
01895 snprintf(buf, sizeof(buf), "%10.2f%%", ((double) stat.bytes / total.bytes * 100));
01896 std::cout << std::right << buf << std::endl;
01897 }
01898 }
01899
01900
01901
01902 typedef pair<const char *, OriginStats *>OriginPair;
01903 inline bool
01904 operator<(const OriginPair &a, const OriginPair &b)
01905 {
01906 return a.second->total.count > b.second->total.count;
01907 }
01908
01909 void
01910 print_detail_stats(const OriginStats * stat, bool json=false)
01911 {
01912
01913 if (!json)
01914 format_detail_header("Request Result");
01915
01916 format_line(json ? "hit.direct" : "Cache hit", stat->results.hits.hit, stat->total, json);
01917 format_line(json ? "hit.ims" : "Cache hit IMS", stat->results.hits.ims, stat->total,json);
01918 format_line(json ? "hit.refresh" : "Cache hit refresh", stat->results.hits.refresh, stat->total,json);
01919 format_line(json ? "hit.other" : "Cache hit other", stat->results.hits.other, stat->total, json);
01920 format_line(json ? "hit.total" : "Cache hit total", stat->results.hits.total, stat->total, json);
01921
01922 if (!json)
01923 std::cout << std::endl;
01924
01925 format_line(json ? "miss.direct" : "Cache miss", stat->results.misses.miss, stat->total, json);
01926 format_line(json ? "miss.ims" : "Cache miss IMS", stat->results.misses.ims, stat->total, json);
01927 format_line(json ? "miss.refresh" : "Cache miss refresh", stat->results.misses.refresh, stat->total, json);
01928 format_line(json ? "miss.other" : "Cache miss other", stat->results.misses.other, stat->total, json);
01929 format_line(json ? "miss.total" : "Cache miss total", stat->results.misses.total, stat->total, json);
01930
01931 if (!json)
01932 std::cout << std::endl;
01933
01934 format_line(json ? "error.client_abort" : "Client aborted", stat->results.errors.client_abort, stat->total, json);
01935 format_line(json ? "error.connect_failed" : "Connect failed", stat->results.errors.connect_fail, stat->total, json);
01936 format_line(json ? "error.invalid_request" : "Invalid request", stat->results.errors.invalid_req, stat->total, json);
01937 format_line(json ? "error.unknown" : "Unknown error(99)", stat->results.errors.unknown, stat->total, json);
01938 format_line(json ? "error.other" : "Other errors", stat->results.errors.other, stat->total, json);
01939 format_line(json ? "error.total" : "Errors total", stat->results.errors.total, stat->total, json);
01940
01941 if (!json) {
01942 std::cout << std::setw(cl.line_len) << std::setfill('.') << '.' << std::setfill(' ') << std::endl;
01943 format_line("Total requests", stat->total, stat->total);
01944 std::cout << std::endl << std::endl;
01945
01946
01947 format_detail_header("HTTP return codes");
01948 }
01949
01950 format_line(json ? "status.100" : "100 Continue", stat->codes.c_100, stat->total, json);
01951
01952 format_line(json ? "status.200" : "200 OK", stat->codes.c_200, stat->total, json);
01953 format_line(json ? "status.201" : "201 Created", stat->codes.c_201, stat->total, json);
01954 format_line(json ? "status.202" : "202 Accepted", stat->codes.c_202, stat->total, json);
01955 format_line(json ? "status.203" : "203 Non-Authoritative Info", stat->codes.c_203, stat->total, json);
01956 format_line(json ? "status.204" : "204 No content", stat->codes.c_204, stat->total, json);
01957 format_line(json ? "status.205" : "205 Reset Content", stat->codes.c_205, stat->total, json);
01958 format_line(json ? "status.206" : "206 Partial content", stat->codes.c_206, stat->total, json);
01959 format_line(json ? "status.2xx" : "2xx Total", stat->codes.c_2xx, stat->total, json);
01960
01961 if (!json)
01962 std::cout << std::endl;
01963
01964 format_line(json ? "status.300" : "300 Multiple Choices", stat->codes.c_300, stat->total, json);
01965 format_line(json ? "status.301" : "301 Moved permanently", stat->codes.c_301, stat->total, json);
01966 format_line(json ? "status.302" : "302 Found", stat->codes.c_302, stat->total, json);
01967 format_line(json ? "status.303" : "303 See Other", stat->codes.c_303, stat->total, json);
01968 format_line(json ? "status.304" : "304 Not modified", stat->codes.c_304, stat->total, json);
01969 format_line(json ? "status.305" : "305 Use Proxy", stat->codes.c_305, stat->total, json);
01970 format_line(json ? "status.307" : "307 Temporary Redirect", stat->codes.c_307, stat->total, json);
01971 format_line(json ? "status.3xx" : "3xx Total", stat->codes.c_3xx, stat->total, json);
01972
01973 if (!json)
01974 std::cout << std::endl;
01975
01976 format_line(json ? "status.400" : "400 Bad request", stat->codes.c_400, stat->total, json);
01977 format_line(json ? "status.401" : "401 Unauthorized", stat->codes.c_401, stat->total, json);
01978 format_line(json ? "status.402" : "402 Payment Required", stat->codes.c_402, stat->total, json);
01979 format_line(json ? "status.403" : "403 Forbidden", stat->codes.c_403, stat->total, json);
01980 format_line(json ? "status.404" : "404 Not found", stat->codes.c_404, stat->total, json);
01981 format_line(json ? "status.405" : "405 Method Not Allowed", stat->codes.c_405, stat->total, json);
01982 format_line(json ? "status.406" : "406 Not Acceptable", stat->codes.c_406, stat->total, json);
01983 format_line(json ? "status.407" : "407 Proxy Auth Required", stat->codes.c_407, stat->total, json);
01984 format_line(json ? "status.408" : "408 Request Timeout", stat->codes.c_408, stat->total, json);
01985 format_line(json ? "status.409" : "409 Conflict", stat->codes.c_409, stat->total, json);
01986 format_line(json ? "status.410" : "410 Gone", stat->codes.c_410, stat->total, json);
01987 format_line(json ? "status.411" : "411 Length Required", stat->codes.c_411, stat->total, json);
01988 format_line(json ? "status.412" : "412 Precondition Failed", stat->codes.c_412, stat->total, json);
01989 format_line(json ? "status.413" : "413 Request Entity Too Large", stat->codes.c_413, stat->total, json);
01990 format_line(json ? "status.414" : "414 Request-URI Too Long", stat->codes.c_414, stat->total, json);
01991 format_line(json ? "status.415" : "415 Unsupported Media Type", stat->codes.c_415, stat->total, json);
01992 format_line(json ? "status.416" : "416 Req Range Not Satisfiable", stat->codes.c_416, stat->total, json);
01993 format_line(json ? "status.417" : "417 Expectation Failed", stat->codes.c_417, stat->total, json);
01994 format_line(json ? "status.4xx" : "4xx Total", stat->codes.c_4xx, stat->total, json);
01995
01996 if (!json)
01997 std::cout << std::endl;
01998
01999 format_line(json ? "status.500" : "500 Internal Server Error", stat->codes.c_500, stat->total, json);
02000 format_line(json ? "status.501" : "501 Not implemented", stat->codes.c_501, stat->total, json);
02001 format_line(json ? "status.502" : "502 Bad gateway", stat->codes.c_502, stat->total, json);
02002 format_line(json ? "status.503" : "503 Service unavailable", stat->codes.c_503, stat->total, json);
02003 format_line(json ? "status.504" : "504 Gateway Timeout", stat->codes.c_504, stat->total, json);
02004 format_line(json ? "status.505" : "505 HTTP Ver. Not Supported", stat->codes.c_505, stat->total, json);
02005 format_line(json ? "status.5xx" : "5xx Total", stat->codes.c_5xx, stat->total, json);
02006
02007 if (!json)
02008 std::cout << std::endl;
02009
02010 format_line(json ? "status.000" : "000 Unknown", stat->codes.c_000, stat->total, json);
02011
02012 if (!json) {
02013 std::cout << std::endl << std::endl;
02014
02015
02016 format_detail_header("Origin hierarchies");
02017 }
02018
02019 format_line(json ? "hier.none" : "NONE", stat->hierarchies.none, stat->total, json);
02020 format_line(json ? "hier.direct" : "DIRECT", stat->hierarchies.direct, stat->total, json);
02021 format_line(json ? "hier.sibling" : "SIBLING", stat->hierarchies.sibling, stat->total, json);
02022 format_line(json ? "hier.parent" : "PARENT", stat->hierarchies.parent, stat->total, json);
02023 format_line(json ? "hier.empty" : "EMPTY", stat->hierarchies.empty, stat->total, json);
02024 format_line(json ? "hier.invalid" : "invalid", stat->hierarchies.invalid, stat->total, json);
02025 format_line(json ? "hier.other" : "other", stat->hierarchies.other, stat->total, json);
02026
02027 if (!json) {
02028 std::cout << std::endl << std::endl;
02029
02030
02031 format_detail_header("HTTP Methods");
02032 }
02033
02034 format_line(json ? "method.options" : "OPTIONS", stat->methods.options, stat->total, json);
02035 format_line(json ? "method.get" : "GET", stat->methods.get, stat->total, json);
02036 format_line(json ? "method.head" : "HEAD", stat->methods.head, stat->total, json);
02037 format_line(json ? "method.post" : "POST", stat->methods.post, stat->total, json);
02038 format_line(json ? "method.put" : "PUT", stat->methods.put, stat->total, json);
02039 format_line(json ? "method.delete" : "DELETE", stat->methods.del, stat->total, json);
02040 format_line(json ? "method.trace" : "TRACE", stat->methods.trace, stat->total, json);
02041 format_line(json ? "method.connect" : "CONNECT", stat->methods.connect, stat->total, json);
02042 format_line(json ? "method.purge" : "PURGE", stat->methods.purge, stat->total, json);
02043 format_line(json ? "method.none" : "none (-)", stat->methods.none, stat->total, json);
02044 format_line(json ? "method.other" : "other", stat->methods.other, stat->total, json);
02045
02046 if (!json) {
02047 std::cout << std::endl << std::endl;
02048
02049
02050 format_detail_header("URL Schemes");
02051 }
02052
02053 format_line(json ? "scheme.http" : "HTTP (port 80)", stat->schemes.http, stat->total, json);
02054 format_line(json ? "scheme.https" : "HTTPS (port 443)", stat->schemes.https, stat->total, json);
02055 format_line(json ? "scheme.none" : "none", stat->schemes.none, stat->total, json);
02056 format_line(json ? "scheme.other" : "other", stat->schemes.other, stat->total, json);
02057
02058 if (!json) {
02059 std::cout << std::endl << std::endl;
02060
02061
02062 format_detail_header("Content Types");
02063 }
02064
02065 format_line(json ? "content.text.javascript" : "text/javascript", stat->content.text.javascript, stat->total, json);
02066 format_line(json ? "content.text.css" : "text/css", stat->content.text.css, stat->total, json);
02067 format_line(json ? "content.text.html" : "text/html", stat->content.text.html, stat->total, json);
02068 format_line(json ? "content.text.xml" : "text/xml", stat->content.text.xml, stat->total, json);
02069 format_line(json ? "content.text.plain" : "text/plain", stat->content.text.plain, stat->total, json);
02070 format_line(json ? "content.text.other" : "text/ other", stat->content.text.other, stat->total, json);
02071 format_line(json ? "content.text.total" : "text/ total", stat->content.text.total, stat->total, json);
02072
02073 if (!json)
02074 std::cout << std::endl;
02075
02076 format_line(json ? "content.image.jpeg" : "image/jpeg", stat->content.image.jpeg, stat->total, json);
02077 format_line(json ? "content.image.gif" : "image/gif", stat->content.image.gif, stat->total, json);
02078 format_line(json ? "content.image.png" : "image/png", stat->content.image.png, stat->total, json);
02079 format_line(json ? "content.image.bmp" : "image/bmp", stat->content.image.bmp, stat->total, json);
02080 format_line(json ? "content.image.other" : "image/ other", stat->content.image.other, stat->total, json);
02081 format_line(json ? "content.image.total" : "image/ total", stat->content.image.total, stat->total, json);
02082
02083 if (!json)
02084 std::cout << std::endl;
02085
02086 format_line(json ? "content.audio.x-wav" : "audio/x-wav", stat->content.audio.wav, stat->total, json);
02087 format_line(json ? "content.audio.x-mpeg" : "audio/x-mpeg", stat->content.audio.mpeg, stat->total, json);
02088 format_line(json ? "content.audio.other" : "audio/ other", stat->content.audio.other, stat->total, json);
02089 format_line(json ? "content.audio.total": "audio/ total", stat->content.audio.total, stat->total, json);
02090
02091 if (!json)
02092 std::cout << std::endl;
02093
02094 format_line(json ? "content.application.shockwave" : "application/x-shockwave", stat->content.application.shockwave_flash, stat->total, json);
02095 format_line(json ? "content.application.javascript" : "application/[x-]javascript", stat->content.application.javascript, stat->total, json);
02096 format_line(json ? "content.application.quicktime" : "application/x-quicktime", stat->content.application.quicktime, stat->total, json);
02097 format_line(json ? "content.application.zip" : "application/zip", stat->content.application.zip, stat->total, json);
02098 format_line(json ? "content.application.rss_xml" : "application/rss+xml", stat->content.application.rss_xml, stat->total, json);
02099 format_line(json ? "content.application.rss_atom" : "application/rss+atom", stat->content.application.rss_atom, stat->total, json);
02100 format_line(json ? "content.application.other" : "application/ other", stat->content.application.other, stat->total, json);
02101 format_line(json ? "content.application.total": "application/ total", stat->content.application.total, stat->total, json);
02102
02103 if (!json)
02104 std::cout << std::endl;
02105
02106 format_line(json ? "content.none" : "none", stat->content.none, stat->total, json);
02107 format_line(json ? "content.other" : "other", stat->content.other, stat->total, json);
02108
02109 if (!json) {
02110 std::cout << std::endl << std::endl;
02111
02112
02113 format_elapsed_header();
02114 }
02115
02116 format_elapsed_line(json ? "hit.direct.latency" : "Cache hit", stat->elapsed.hits.hit, json);
02117 format_elapsed_line(json ? "hit.ims.latency" : "Cache hit IMS", stat->elapsed.hits.ims, json);
02118 format_elapsed_line(json ? "hit.refresh.latency" : "Cache hit refresh", stat->elapsed.hits.refresh, json);
02119 format_elapsed_line(json ? "hit.other.latency" : "Cache hit other", stat->elapsed.hits.other, json);
02120 format_elapsed_line(json ? "hit.total.latency" : "Cache hit total", stat->elapsed.hits.total, json);
02121
02122 format_elapsed_line(json ? "miss.direct.latency" : "Cache miss", stat->elapsed.misses.miss, json);
02123 format_elapsed_line(json ? "miss.ims.latency" : "Cache miss IMS", stat->elapsed.misses.ims, json);
02124 format_elapsed_line(json ? "miss.refresh.latency" : "Cache miss refresh", stat->elapsed.misses.refresh, json);
02125 format_elapsed_line(json ? "miss.other.latency" : "Cache miss other", stat->elapsed.misses.other, json);
02126 format_elapsed_line(json ? "miss.total.latency" : "Cache miss total", stat->elapsed.misses.total, json);
02127
02128 if (!json) {
02129 std::cout << std::endl;
02130 std::cout << std::setw(cl.line_len) << std::setfill('_') << '_' << std::setfill(' ') << std::endl;
02131 } else {
02132 std::cout << " \"_timestamp\" : \"" << static_cast<int>(ink_time_wall_seconds()) << '"' << std::endl;
02133 }
02134 }
02135
02136
02137
02138
02139 void
02140 my_exit(const ExitStatus& status)
02141 {
02142 vector<OriginPair> vec;
02143 bool first = true;
02144 int max_origins;
02145
02146
02147 if (urls) {
02148 urls->dump(cl.as_object);
02149 if (cl.as_object)
02150 std::cout << "}" << std::endl;
02151 else
02152 std::cout << "]" << std::endl;
02153 _exit(status.level);
02154 }
02155
02156 if (cl.json) {
02157
02158 } else {
02159 switch (status.level) {
02160 case EXIT_OK:
02161 break;
02162 case EXIT_WARNING:
02163 std::cout << "warning: " << status.notice << std::endl;
02164 break;
02165 case EXIT_CRITICAL:
02166 std::cout << "critical: " << status.notice << std::endl;
02167 _exit(status.level);
02168 break;
02169 case EXIT_UNKNOWN:
02170 std::cout << "unknown: " << status.notice << std::endl;
02171 _exit(status.level);
02172 break;
02173 }
02174 }
02175
02176 if (!origins.empty()) {
02177
02178 for (OriginStorage::iterator i = origins.begin(); i != origins.end(); i++)
02179 if (use_origin(i->second))
02180 vec.push_back(*i);
02181 sort(vec.begin(), vec.end());
02182
02183 if (!cl.json) {
02184
02185 format_center("Traffic summary");
02186 std::cout << std::left << std::setw(33) << "Origin Server";
02187 std::cout << std::right << std::setw(15) << "Hits";
02188 std::cout << std::right << std::setw(15) << "Misses";
02189 std::cout << std::right << std::setw(15) << "Errors" << std::endl;
02190 std::cout << std::setw(cl.line_len) << std::setfill('-') << '-' << std::setfill(' ') << std::endl;
02191
02192 max_origins = cl.max_origins > 0 ? cl.max_origins : INT_MAX;
02193 for (vector<OriginPair>::iterator i = vec.begin(); (i != vec.end()) && (max_origins > 0); ++i, --max_origins) {
02194 std::cout << std::left << std::setw(33) << i->first;
02195 std::cout << std::right << std::setw(15);
02196 format_int(i->second->results.hits.total.count);
02197 std::cout << std::right << std::setw(15);
02198 format_int(i->second->results.misses.total.count);
02199 std::cout << std::right << std::setw(15);
02200 format_int(i->second->results.errors.total.count);
02201 std::cout << std::endl;
02202 }
02203 std::cout << std::setw(cl.line_len) << std::setfill('=') << '=' << std::setfill(' ') << std::endl;
02204 std::cout << std::endl << std::endl << std::endl;
02205 }
02206 }
02207
02208
02209 if (origin_set->empty()) {
02210 first = false;
02211 if (cl.json) {
02212 std::cout << "{ \"total\": {" << std::endl;
02213 print_detail_stats(&totals, cl.json);
02214 std::cout << " }";
02215 } else {
02216 format_center("Totals (all Origins combined)");
02217 print_detail_stats(&totals);
02218 std::cout << std::endl << std::endl << std::endl;
02219 }
02220 }
02221
02222
02223 max_origins = cl.max_origins > 0 ? cl.max_origins : INT_MAX;
02224 for (vector<OriginPair>::iterator i = vec.begin(); (i != vec.end()) && (max_origins > 0); ++i, --max_origins) {
02225 if (cl.json) {
02226 if (first) {
02227 std::cout << "{ ";
02228 first = false;
02229 } else {
02230 std::cout << "," << std::endl << " ";
02231 }
02232 std::cout << '"' << i->first << "\": {" << std::endl;
02233 print_detail_stats(i->second, cl.json);
02234 std::cout << " }";
02235 } else {
02236 format_center(i->first);
02237 print_detail_stats(i->second);
02238 std::cout << std::endl << std::endl << std::endl;
02239 }
02240 }
02241
02242 if (cl.json) {
02243 std::cout << std::endl << "}" << std::endl;
02244 }
02245
02246 _exit(status.level);
02247 }
02248
02249
02250
02251 int
02252 open_main_log(ExitStatus& status)
02253 {
02254 std::string logfile(Layout::get()->logdir);
02255 int cnt = 3;
02256 int main_fd;
02257
02258 logfile.append("/squid.blog");
02259 while (((main_fd = open(logfile.c_str(), O_RDONLY)) < 0) && --cnt) {
02260 switch (errno) {
02261 case ENOENT:
02262 case EACCES:
02263 sleep(5);
02264 break;
02265 default:
02266 status.append(" can't open squid.blog");
02267 return -1;
02268 }
02269 }
02270
02271 if (main_fd < 0) {
02272 status.append(" squid.blog not enabled");
02273 return -1;
02274 }
02275 #if HAVE_POSIX_FADVISE
02276 posix_fadvise(main_fd, 0, 0, POSIX_FADV_DONTNEED);
02277 #endif
02278 return main_fd;
02279 }
02280
02281
02282
02283
02284
02285 int
02286 main(int , char *argv[])
02287 {
02288 ExitStatus exit_status;
02289 int res, cnt;
02290 int main_fd;
02291 unsigned max_age;
02292 struct flock lck;
02293
02294
02295 appVersionInfo.setup(PACKAGE_NAME,PROGRAM_NAME, PACKAGE_VERSION, __DATE__, __TIME__,
02296 BUILD_MACHINE, BUILD_PERSON, "");
02297
02298
02299 Layout::create();
02300
02301 memset(&totals, 0, sizeof(totals));
02302 init_elapsed(&totals);
02303
02304 origin_set = new OriginSet;
02305 parse_errors = 0;
02306
02307
02308 cl.parse_arguments(argv);
02309
02310
02311 if (cl.max_age > 0) {
02312 struct timeval tv;
02313
02314 gettimeofday(&tv, NULL);
02315 max_age = tv.tv_sec - cl.max_age;
02316 } else {
02317 max_age = 0;
02318 }
02319
02320
02321 init_log_standalone_basic(PROGRAM_NAME);
02322 Log::init(Log::NO_REMOTE_MANAGEMENT | Log::LOGCAT);
02323
02324
02325 if (cl.origin_list[0] != '\0') {
02326 char *tok;
02327 char *sep_ptr;
02328
02329 for (tok = strtok_r(cl.origin_list, ",", &sep_ptr); tok != NULL;) {
02330 origin_set->insert(tok);
02331 tok = strtok_r(NULL, ",", &sep_ptr);
02332 }
02333 }
02334
02335 if (cl.origin_file[0] != '\0') {
02336 std::ifstream fs;
02337
02338 fs.open(cl.origin_file, std::ios::in);
02339 if (!fs.is_open()) {
02340 std::cerr << "can't read " << cl.origin_file << std::endl;
02341 usage(argument_descriptions, countof(argument_descriptions), USAGE_LINE);
02342 _exit(0);
02343 }
02344
02345 while (!fs.eof()) {
02346 std::string line;
02347 std::string::size_type start, end;
02348
02349 getline(fs, line);
02350 start = line.find_first_not_of(" \t");
02351 if (start != std::string::npos) {
02352 end = line.find_first_of(" \t#/");
02353 if (std::string::npos == end)
02354 end = line.length();
02355
02356 if (end > start) {
02357 char *buf;
02358
02359 buf = ats_strdup(line.substr(start, end).c_str());
02360 if (buf) {
02361 origin_set->insert(buf);
02362 }
02363 }
02364 }
02365 }
02366 }
02367
02368
02369 if (cl.cgi) {
02370 std::cout << "Content-Type: application/javascript\r\n";
02371 std::cout << "Cache-Control: no-cache\r\n\r\n";
02372 }
02373
02374
02375 if (cl.urls != 0) {
02376 urls = new UrlLru(cl.urls, cl.show_urls);
02377 if (cl.as_object)
02378 std::cout << "{" << std::endl;
02379 else
02380 std::cout << "[" << std::endl;
02381 }
02382
02383
02384 if (cl.incremental) {
02385
02386 if (chdir(Layout::get()->logdir) < 0) {
02387 exit_status.set(EXIT_CRITICAL, " can't chdir to ");
02388 exit_status.append(Layout::get()->logdir);
02389 my_exit(exit_status);
02390 }
02391
02392 std::string sf_name(Layout::get()->logdir);
02393 struct stat stat_buf;
02394 int state_fd;
02395 sf_name.append("/logstats.state");
02396
02397 if (cl.state_tag[0] != '\0') {
02398 sf_name.append(".");
02399 sf_name.append(cl.state_tag);
02400 } else {
02401
02402 struct passwd *pwd = getpwuid(geteuid());
02403
02404 if (pwd) {
02405 sf_name.append(".");
02406 sf_name.append(pwd->pw_name);
02407 } else {
02408 exit_status.set(EXIT_CRITICAL, " can't get current UID");
02409 my_exit(exit_status);
02410 }
02411 }
02412
02413 if ((state_fd = open(sf_name.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
02414 exit_status.set(EXIT_CRITICAL, " can't open state file ");
02415 exit_status.append(sf_name);
02416 my_exit(exit_status);
02417 }
02418
02419
02420 lck.l_type = F_WRLCK;
02421 lck.l_whence = 0;
02422 lck.l_start = (off_t)0;
02423 lck.l_len = (off_t)0;
02424 cnt = 10;
02425 while (((res = fcntl(state_fd, F_SETLK, &lck)) < 0) && --cnt) {
02426 switch (errno) {
02427 case EWOULDBLOCK:
02428 case EINTR:
02429 sleep(2);
02430 break;
02431 default:
02432 exit_status.set(EXIT_CRITICAL, " locking failure");
02433 my_exit(exit_status);
02434 break;
02435 }
02436 }
02437
02438 if (res < 0) {
02439 exit_status.set(EXIT_CRITICAL, " can't lock state file");
02440 my_exit(exit_status);
02441 }
02442
02443 cnt = 10;
02444 while (((res = read(state_fd, &last_state, sizeof(last_state))) < 0) && --cnt) {
02445 switch (errno) {
02446 case EINTR:
02447 case EAGAIN:
02448 sleep(1);
02449 break;
02450 default:
02451 exit_status.set(EXIT_CRITICAL, " can't read state file");
02452 my_exit(exit_status);
02453 break;
02454 }
02455 }
02456
02457 if (res != sizeof(last_state)) {
02458
02459 last_state.offset = 0;
02460 last_state.st_ino = 0;
02461 }
02462
02463 if ((main_fd = open_main_log(exit_status)) < 0) {
02464 exit_status.set(EXIT_CRITICAL);
02465 my_exit(exit_status);
02466 }
02467
02468
02469 if (fstat(main_fd, &stat_buf) < 0) {
02470 exit_status.set(EXIT_CRITICAL, " can't stat squid.blog");
02471 my_exit(exit_status);
02472 }
02473
02474 if (last_state.st_ino <= 0)
02475 last_state.st_ino = stat_buf.st_ino;
02476
02477
02478
02479 if (stat_buf.st_ino != last_state.st_ino) {
02480 DIR *dirp = NULL;
02481 struct dirent *dp = NULL;
02482 ino_t old_inode = last_state.st_ino;
02483
02484
02485 last_state.st_ino = stat_buf.st_ino;
02486
02487
02488 dirp = opendir(Layout::get()->logdir);
02489 if (NULL == dirp) {
02490 exit_status.set(EXIT_WARNING, " can't read log directory");
02491 } else {
02492 while ((dp = readdir(dirp)) != NULL) {
02493 if (stat(dp->d_name, &stat_buf) < 0) {
02494 exit_status.set(EXIT_WARNING, " can't stat ");
02495 exit_status.append(dp->d_name);
02496 } else if (stat_buf.st_ino == old_inode) {
02497 int old_fd = open(dp->d_name, O_RDONLY);
02498
02499 if (old_fd < 0) {
02500 exit_status.set(EXIT_WARNING, " can't open ");
02501 exit_status.append(dp->d_name);
02502 break;
02503 }
02504
02505 if (process_file(old_fd, last_state.offset, max_age) != 0) {
02506 exit_status.set(EXIT_WARNING, " can't read ");
02507 exit_status.append(dp->d_name);
02508 }
02509 close(old_fd);
02510 break;
02511 }
02512 }
02513 }
02514
02515 last_state.offset = 0;
02516 } else {
02517
02518 if (last_state.offset > stat_buf.st_size)
02519 last_state.offset = stat_buf.st_size;
02520 }
02521
02522
02523 if (process_file(main_fd, last_state.offset, max_age) != 0) {
02524 exit_status.set(EXIT_CRITICAL, " can't parse log");
02525 last_state.offset = 0;
02526 last_state.st_ino = 0;
02527 } else {
02528
02529 last_state.offset = lseek(main_fd, 0, SEEK_CUR);
02530 if (last_state.offset < 0) {
02531 exit_status.set(EXIT_WARNING, " can't lseek squid.blog");
02532 last_state.offset = 0;
02533 }
02534 }
02535
02536
02537 if (lseek(state_fd, 0, SEEK_SET) < 0) {
02538 exit_status.set(EXIT_WARNING, " can't lseek state file");
02539 } else {
02540 if (-1 == write(state_fd, &last_state, sizeof(last_state))) {
02541 exit_status.set(EXIT_WARNING, " can't write state_fd ");
02542 }
02543 }
02544
02545 lck.l_type = F_UNLCK;
02546 fcntl(state_fd, F_SETLK, &lck);
02547 close(main_fd);
02548 close(state_fd);
02549 } else {
02550 if (cl.log_file[0] != '\0') {
02551 main_fd = open(cl.log_file, O_RDONLY);
02552 if (main_fd < 0) {
02553 exit_status.set(EXIT_CRITICAL, " can't open log file ");
02554 exit_status.append(cl.log_file);
02555 my_exit(exit_status);
02556 }
02557 } else {
02558 main_fd = open_main_log(exit_status);
02559 }
02560
02561 if (cl.tail > 0) {
02562 if (lseek(main_fd, 0, SEEK_END) < 0) {
02563 exit_status.set(EXIT_CRITICAL, " can't lseek squid.blog");
02564 my_exit(exit_status);
02565 }
02566 sleep(cl.tail);
02567 }
02568
02569 if (process_file(main_fd, 0, max_age) != 0) {
02570 close(main_fd);
02571 exit_status.set(EXIT_CRITICAL, " can't parse log file ");
02572 exit_status.append(cl.log_file);
02573 my_exit(exit_status);
02574 }
02575 close(main_fd);
02576 }
02577
02578
02579 if (EXIT_OK == exit_status.level)
02580 exit_status.append(" OK");
02581 my_exit(exit_status);
02582 }