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

Generated on Thu May 25 21:51:03 2006 for WvStreams by  doxygen 1.4.6