WvStreams
wvlog.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Functions needed to implement general WvLog class.
00006  * 
00007  * See wvlog.h for more information.
00008  */
00009 #include "wvlogrcv.h"
00010 #include "wvstringlist.h"
00011 #include "strutils.h"
00012 #include "wvfork.h"
00013 
00014 #include <ctype.h>
00015 
00016 #ifdef _WIN32
00017 #define snprintf _snprintf
00018 #endif
00019 
00020 WvLogRcvBaseList *WvLog::receivers;
00021 int WvLog::num_receivers = 0, WvLog::num_logs = 0;
00022 WvLogRcvBase *WvLog::default_receiver = NULL;
00023 
00024 const char *WvLogRcv::loglevels[WvLog::NUM_LOGLEVELS] = {
00025     "Crit",
00026     "Err",
00027     "Warn",
00028     "Notice",
00029     "Info",
00030     "*1",
00031     "*2",
00032     "*3",
00033     "*4",
00034     "*5",
00035 };
00036 
00037 
00038 
00040 
00041 
00042 
00043 WvLog::WvLog(WvStringParm _app, LogLevel _loglevel, WvLogFilter* _filter)
00044     : app(_app), loglevel(_loglevel), filter(_filter)
00045 {
00046 //    printf("log: %s create\n", app.cstr());
00047     num_logs++;
00048     set_wsname(app);
00049 }
00050 
00051 
00052 WvLog::WvLog(const WvLog &l)
00053     : app(l.app), loglevel(l.loglevel), filter(l.filter)
00054 {
00055 //    printf("log: %s create\n", app.cstr());
00056     num_logs++;
00057     set_wsname(app);
00058 }
00059 
00060 
00061 WvLog::~WvLog()
00062 {
00063     num_logs--;
00064     if (!num_logs && default_receiver)
00065     {
00066         num_receivers++; // deleting default does not really reduce
00067         delete default_receiver;
00068         default_receiver = NULL;
00069     }
00070 //    printf("log: %s delete\n", app.cstr());
00071 //    printf("num_logs is now %d\n", num_logs);
00072 }
00073 
00074 
00075 bool WvLog::isok() const
00076 {
00077     return true;
00078 }
00079 
00080 
00081 void WvLog::pre_select(SelectInfo &si)
00082 {
00083     // a wvlog is always writable...
00084     if (si.wants.writable)
00085         si.msec_timeout = 0;
00086     else
00087         WvStream::pre_select(si);
00088 }
00089 
00090 
00091 bool WvLog::post_select(SelectInfo &si)
00092 {
00093     // a wvlog is always writable...
00094     if (si.wants.writable)
00095         return true;
00096     else
00097         return WvStream::post_select(si);
00098 }
00099 
00100 
00101 size_t WvLog::uwrite(const void *_buf, size_t len)
00102 {
00103     // Writing the log message to a stream might cause it to emit its own log
00104     // messages, causing recursion.  Don't let it get out of hand.
00105     static const int recursion_max = 8;
00106     static int recursion_count = 0;
00107     static WvString recursion_msg("Too many extra log messages written while "
00108             "writing to the log.  Suppressing additional messages.\n");
00109 
00110     ++recursion_count;
00111 
00112     if (!num_receivers)
00113     {
00114         if (!default_receiver)
00115         {
00116             // nobody's listening -- create a receiver on the console
00117             int xfd = dup(2);
00118             default_receiver = new WvLogConsole(xfd);
00119             num_receivers--; // default does not qualify!
00120         }
00121 
00122         if (recursion_count < recursion_max)
00123             default_receiver->log(app, loglevel, (const char *)_buf, len);
00124         else if (recursion_count == recursion_max)
00125             default_receiver->log(app, WvLog::Warning, recursion_msg.cstr(),
00126                     recursion_msg.len());
00127 
00128         --recursion_count;
00129         return len;
00130     }
00131     else if (default_receiver)
00132     {
00133         // no longer empty list -- delete our default to stderr
00134         num_receivers++; // deleting default does not really reduce
00135         delete default_receiver;
00136         default_receiver = NULL;
00137     }
00138     
00139     assert(receivers);
00140     WvLogRcvBaseList::Iter i(*receivers);
00141     for (i.rewind(); i.next(); )
00142     {
00143         WvLogRcvBase &rc = *i;
00144 
00145         if (recursion_count < recursion_max)
00146             rc.log(app, loglevel, (const char *)_buf, len);
00147         else if (recursion_count == recursion_max)
00148             rc.log(app, WvLog::Warning, recursion_msg.cstr(), 
00149                     recursion_msg.len());
00150     }
00151     
00152     --recursion_count;
00153     return len;
00154 }
00155 
00156 
00157 
00159 
00160 
00161 
00162 WvLogRcvBase::WvLogRcvBase()
00163 {
00164     static_init();
00165     WvLogRcvBase::force_new_line = false;
00166     if (!WvLog::receivers)
00167         WvLog::receivers = new WvLogRcvBaseList;
00168     WvLog::receivers->append(this, false);
00169     WvLog::num_receivers++;
00170 }
00171 
00172 
00173 WvLogRcvBase::~WvLogRcvBase()
00174 {
00175     assert(WvLog::receivers);
00176     WvLog::receivers->unlink(this);
00177     if (WvLog::receivers->isempty())
00178     {
00179         delete WvLog::receivers;
00180         WvLog::receivers = NULL;
00181     }
00182     WvLog::num_receivers--;
00183 }
00184 
00185 
00186 const char *WvLogRcvBase::appname(WvStringParm log) const
00187 {
00188     if (log)
00189         return log;
00190     else
00191         return "unknown";
00192 }
00193 
00194 
00195 void WvLogRcvBase::static_init()
00196 {
00197     static bool init = false;
00198     if (!init)
00199     {
00200 #ifndef _WIN32
00201         add_wvfork_callback(WvLogRcvBase::cleanup_on_fork);
00202 #endif
00203         init = true;
00204     }
00205 }
00206 
00207 
00208 void WvLogRcvBase::cleanup_on_fork(pid_t p)
00209 {
00210     if (p) return;      // parent: do nothing
00211 
00212     if (WvLog::receivers)
00213         WvLog::receivers->zap();
00214     delete WvLog::default_receiver;
00215     WvLog::default_receiver = NULL;
00216     WvLog::num_receivers = 0;
00217 }
00218 
00219 
00220 
00222 
00223 
00224 
00225 WvLogRcv::WvLogRcv(WvLog::LogLevel _max_level) : custom_levels(5)
00226 {
00227     last_source = WvString();
00228     last_level = WvLog::NUM_LOGLEVELS;
00229     last_time = 0;
00230     max_level = _max_level;
00231     at_newline = true;
00232 }
00233 
00234 
00235 WvLogRcv::~WvLogRcv()
00236 {
00237 }
00238 
00239 
00240 void WvLogRcv::_make_prefix(time_t now)
00241 {
00242     prefix = WvString("%s<%s>: ",
00243         last_source, loglevels[last_level]);
00244     prelen = prefix.len();
00245 }
00246 
00247 
00248 void WvLogRcv::_begin_line()
00249 {
00250     mid_line(prefix, prelen);
00251 }
00252 
00253 
00254 void WvLogRcv::_end_line()
00255 {
00256     // do nothing
00257 }
00258 
00259 
00260 // like isprint(), but always treats chars >128 as printable, because they
00261 // always are (even if they're meaningless)
00262 static bool my_isprint(char _c)
00263 {
00264     unsigned char c = _c;
00265     if (isprint(c) || c >= 128)
00266         return true;
00267     else
00268         return false;
00269 }
00270 
00271 
00272 void WvLogRcv::log(WvStringParm source, int _loglevel,
00273                         const char *_buf, size_t len)
00274 {
00275     WvLog::LogLevel loglevel = (WvLog::LogLevel)_loglevel;
00276     char hex[5];
00277     WvLog::LogLevel threshold = max_level;
00278     WvString srcname(source);
00279     strlwr(srcname.edit());
00280 
00281     Src_LvlDict::Iter i(custom_levels);
00282     i.rewind(); 
00283 
00284     // Check if the debug level for the source has been overridden
00285     while (i.next())
00286     {
00287         if (strstr(srcname, i->src))
00288         {
00289             threshold = i->lvl;
00290             break;
00291         }
00292     }
00293      
00294     if (loglevel > threshold)
00295         return;
00296 
00297     // only need to start a new line with new headers if they headers have
00298     // changed.  if the source and level are the same as before, just continue
00299     // the previous log entry.
00300     time_t now = wvtime().tv_sec;
00301     if (source != last_source
00302             || loglevel != last_level
00303             || WvLogRcvBase::force_new_line)
00304     {
00305         end_line();
00306         last_source = source;
00307         last_level = loglevel;
00308         last_time = now;
00309         _make_prefix(now);
00310     }
00311     else if (last_time == 0 || now != last_time)
00312     {
00313         // ensure that even with the same source and level, logs will
00314         // properly get the right time associated with them. however,
00315         // don't split up log messages that should appear in a single
00316         // log line.
00317         last_time = now;
00318         if (at_newline)
00319             _make_prefix(now);
00320     }
00321     
00322     const char *buf = (const char *)_buf, *bufend = buf + len, *cptr;
00323 
00324     // loop through the buffer, printing each character or its [hex] equivalent
00325     // if it is unprintable.  Also eat newlines unless they are appropriate.
00326     while (buf < bufend)
00327     {
00328         if (buf[0] == '\n' || buf[0] == '\r')
00329         {
00330             end_line();
00331             buf++;
00332             continue;
00333         }
00334 
00335         begin_line();
00336 
00337         if (buf[0] == '\t')
00338         {
00339             mid_line(" ", 1);
00340             buf++;
00341             continue;
00342         }
00343         else if (!my_isprint(buf[0]))
00344         {
00345             snprintf(hex, 5, "[%02x]", buf[0]);
00346             mid_line(hex, 4);
00347             buf++;
00348             continue;
00349         }
00350 
00351         // like strchr, but size-limited instead of null-terminated
00352         for (cptr = buf; cptr < bufend; cptr++)
00353         {
00354             if (*cptr == '\n' || !my_isprint(*cptr))
00355                 break;
00356         }
00357         
00358         if (cptr >= bufend) // end of buffer
00359         {
00360             mid_line(buf, bufend - buf);
00361             buf = bufend;
00362         }
00363         else if (*cptr == '\n') // end of line
00364         {
00365             mid_line((const char *)buf, cptr - buf);
00366             buf = cptr;
00367         }
00368         else // therefore (!my_isprint(*cptr))
00369         {
00370             mid_line(buf, cptr - buf);
00371             buf = cptr;
00372         }
00373     }
00374 }
00375 
00376 // input format: name=number, name=number, name=number, etc.
00377 //    'name' is the name of a log service
00378 //    'number' is the number of the log level to use.
00379 bool WvLogRcv::set_custom_levels(WvString descr)
00380 {
00381     custom_levels.zap();
00382 
00383     // Parse the filter line into individual rules
00384     WvStringList lst;
00385     WvStringList::Iter i(lst);
00386     lst.split(descr, ",= ");
00387     if (lst.isempty())
00388         return true;
00389     WvString src("");
00390 
00391     for (i.rewind(); i.next(); )
00392     {
00393         if (src != "")
00394         {
00395             if (atoi(*i) > 0 && atoi(*i) <= WvLog::NUM_LOGLEVELS)
00396             {
00397                 custom_levels.add(new Src_Lvl(src, atoi(*i)), true);
00398                 src = "";
00399             }
00400             else
00401                 return false;
00402         }
00403         else
00404         {
00405             src = *i;
00406             strlwr(trim_string(src.edit()));
00407         }
00408     }
00409     if (src != "")
00410         return false;
00411 
00412     return true;
00413 }
00414 
00415 
00417 
00418 
00419 
00420 WvLogConsole::WvLogConsole(int _fd, WvLog::LogLevel _max_level) :
00421     WvFDStream(_fd), WvLogRcv(_max_level)
00422 {
00423 }
00424 
00425 
00426 WvLogConsole::~WvLogConsole()
00427 {
00428     end_line();
00429 }
00430 
00431 
00432 void WvLogConsole::_mid_line(const char *str, size_t len)
00433 {
00434     uwrite(str, len);
00435 }