WvStreams
|
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 }