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

Generated on Fri Oct 5 18:20:28 2007 for WvStreams by  doxygen 1.5.3