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 }