WvStreams
wvlog.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Functions needed to implement general WvLog class.
6 *
7 * See wvlog.h for more information.
8 */
9#include "wvlogrcv.h"
10#include "wvstringlist.h"
11#include "strutils.h"
12#include "wvfork.h"
13
14#include <ctype.h>
15
16#ifdef _WIN32
17#define snprintf _snprintf
18#endif
19
20WvLogRcvBaseList *WvLog::receivers;
21int WvLog::num_receivers = 0, WvLog::num_logs = 0;
22WvLogRcvBase *WvLog::default_receiver = NULL;
23
24const char *WvLogRcv::loglevels[WvLog::NUM_LOGLEVELS] = {
25 "Crit",
26 "Err",
27 "Warn",
28 "Notice",
29 "Info",
30 "*1",
31 "*2",
32 "*3",
33 "*4",
34 "*5",
35};
36
37
38
40
41
42
43WvLog::WvLog(WvStringParm _app, LogLevel _loglevel, WvLogFilter* _filter)
44 : app(_app), loglevel(_loglevel), filter(_filter)
45{
46// printf("log: %s create\n", app.cstr());
47 num_logs++;
48 set_wsname(app);
49}
50
51
52WvLog::WvLog(const WvLog &l)
53 : app(l.app), loglevel(l.loglevel), filter(l.filter)
54{
55// printf("log: %s create\n", app.cstr());
56 num_logs++;
57 set_wsname(app);
58}
59
60
61WvLog::~WvLog()
62{
63 num_logs--;
64 if (!num_logs && default_receiver)
65 {
66 num_receivers++; // deleting default does not really reduce
67 delete default_receiver;
68 default_receiver = NULL;
69 }
70// printf("log: %s delete\n", app.cstr());
71// printf("num_logs is now %d\n", num_logs);
72}
73
74
75bool WvLog::isok() const
76{
77 return true;
78}
79
80
82{
83 // a wvlog is always writable...
84 if (si.wants.writable)
85 si.msec_timeout = 0;
86 else
88}
89
90
92{
93 // a wvlog is always writable...
94 if (si.wants.writable)
95 return true;
96 else
97 return WvStream::post_select(si);
98}
99
100
101size_t WvLog::uwrite(const void *_buf, size_t len)
102{
103 // Writing the log message to a stream might cause it to emit its own log
104 // messages, causing recursion. Don't let it get out of hand.
105 static const int recursion_max = 8;
106 static int recursion_count = 0;
107 static WvString recursion_msg("Too many extra log messages written while "
108 "writing to the log. Suppressing additional messages.\n");
109
110 ++recursion_count;
111
112 if (!num_receivers)
113 {
114 if (!default_receiver)
115 {
116 // nobody's listening -- create a receiver on the console
117 int xfd = dup(2);
118 default_receiver = new WvLogConsole(xfd);
119 num_receivers--; // default does not qualify!
120 }
121
122 if (recursion_count < recursion_max)
123 default_receiver->log(app, loglevel, (const char *)_buf, len);
124 else if (recursion_count == recursion_max)
125 default_receiver->log(app, WvLog::Warning, recursion_msg.cstr(),
126 recursion_msg.len());
127
128 --recursion_count;
129 return len;
130 }
131 else if (default_receiver)
132 {
133 // no longer empty list -- delete our default to stderr
134 num_receivers++; // deleting default does not really reduce
135 delete default_receiver;
136 default_receiver = NULL;
137 }
138
139 assert(receivers);
140 WvLogRcvBaseList::Iter i(*receivers);
141 for (i.rewind(); i.next(); )
142 {
143 WvLogRcvBase &rc = *i;
144
145 if (recursion_count < recursion_max)
146 rc.log(app, loglevel, (const char *)_buf, len);
147 else if (recursion_count == recursion_max)
148 rc.log(app, WvLog::Warning, recursion_msg.cstr(),
149 recursion_msg.len());
150 }
151
152 --recursion_count;
153 return len;
154}
155
156
157
159
160
161
162WvLogRcvBase::WvLogRcvBase()
163{
164 static_init();
165 WvLogRcvBase::force_new_line = false;
166 if (!WvLog::receivers)
167 WvLog::receivers = new WvLogRcvBaseList;
168 WvLog::receivers->append(this, false);
169 WvLog::num_receivers++;
170}
171
172
173WvLogRcvBase::~WvLogRcvBase()
174{
175 assert(WvLog::receivers);
176 WvLog::receivers->unlink(this);
177 if (WvLog::receivers->isempty())
178 {
179 delete WvLog::receivers;
180 WvLog::receivers = NULL;
181 }
182 WvLog::num_receivers--;
183}
184
185
186const char *WvLogRcvBase::appname(WvStringParm log) const
187{
188 if (log)
189 return log;
190 else
191 return "unknown";
192}
193
194
195void WvLogRcvBase::static_init()
196{
197 static bool init = false;
198 if (!init)
199 {
200#ifndef _WIN32
201 add_wvfork_callback(WvLogRcvBase::cleanup_on_fork);
202#endif
203 init = true;
204 }
205}
206
207
208void WvLogRcvBase::cleanup_on_fork(pid_t p)
209{
210 if (p) return; // parent: do nothing
211
212 if (WvLog::receivers)
213 WvLog::receivers->zap();
214 delete WvLog::default_receiver;
215 WvLog::default_receiver = NULL;
216 WvLog::num_receivers = 0;
217}
218
219
220
222
223
224
225WvLogRcv::WvLogRcv(WvLog::LogLevel _max_level) : custom_levels(5)
226{
227 last_source = WvString();
228 last_level = WvLog::NUM_LOGLEVELS;
229 last_time = 0;
230 max_level = _max_level;
231 at_newline = true;
232}
233
234
235WvLogRcv::~WvLogRcv()
236{
237}
238
239
241{
242 prefix = WvString("%s<%s>: ",
243 last_source, loglevels[last_level]);
244 prelen = prefix.len();
245}
246
247
249{
250 mid_line(prefix, prelen);
251}
252
253
255{
256 // do nothing
257}
258
259
260// like isprint(), but always treats chars >128 as printable, because they
261// always are (even if they're meaningless)
262static bool my_isprint(char _c)
263{
264 unsigned char c = _c;
265 if (isprint(c) || c >= 128)
266 return true;
267 else
268 return false;
269}
270
271
272void WvLogRcv::log(WvStringParm source, int _loglevel,
273 const char *_buf, size_t len)
274{
275 WvLog::LogLevel loglevel = (WvLog::LogLevel)_loglevel;
276 char hex[5];
277 WvLog::LogLevel threshold = max_level;
278 WvString srcname(source);
279 strlwr(srcname.edit());
280
281 Src_LvlDict::Iter i(custom_levels);
282 i.rewind();
283
284 // Check if the debug level for the source has been overridden
285 while (i.next())
286 {
287 if (strstr(srcname, i->src))
288 {
289 threshold = i->lvl;
290 break;
291 }
292 }
293
294 if (loglevel > threshold)
295 return;
296
297 // only need to start a new line with new headers if they headers have
298 // changed. if the source and level are the same as before, just continue
299 // the previous log entry.
300 time_t now = wvtime().tv_sec;
301 if (source != last_source
302 || loglevel != last_level
303 || WvLogRcvBase::force_new_line)
304 {
305 end_line();
306 last_source = source;
307 last_level = loglevel;
308 last_time = now;
309 _make_prefix(now);
310 }
311 else if (last_time == 0 || now != last_time)
312 {
313 // ensure that even with the same source and level, logs will
314 // properly get the right time associated with them. however,
315 // don't split up log messages that should appear in a single
316 // log line.
317 last_time = now;
318 if (at_newline)
319 _make_prefix(now);
320 }
321
322 const char *buf = (const char *)_buf, *bufend = buf + len, *cptr;
323
324 // loop through the buffer, printing each character or its [hex] equivalent
325 // if it is unprintable. Also eat newlines unless they are appropriate.
326 while (buf < bufend)
327 {
328 if (buf[0] == '\n' || buf[0] == '\r')
329 {
330 end_line();
331 buf++;
332 continue;
333 }
334
335 begin_line();
336
337 if (buf[0] == '\t')
338 {
339 mid_line(" ", 1);
340 buf++;
341 continue;
342 }
343 else if (!my_isprint(buf[0]))
344 {
345 snprintf(hex, 5, "[%02x]", buf[0]);
346 mid_line(hex, 4);
347 buf++;
348 continue;
349 }
350
351 // like strchr, but size-limited instead of null-terminated
352 for (cptr = buf; cptr < bufend; cptr++)
353 {
354 if (*cptr == '\n' || !my_isprint(*cptr))
355 break;
356 }
357
358 if (cptr >= bufend) // end of buffer
359 {
360 mid_line(buf, bufend - buf);
361 buf = bufend;
362 }
363 else if (*cptr == '\n') // end of line
364 {
365 mid_line((const char *)buf, cptr - buf);
366 buf = cptr;
367 }
368 else // therefore (!my_isprint(*cptr))
369 {
370 mid_line(buf, cptr - buf);
371 buf = cptr;
372 }
373 }
374}
375
376// input format: name=number, name=number, name=number, etc.
377// 'name' is the name of a log service
378// 'number' is the number of the log level to use.
379bool WvLogRcv::set_custom_levels(WvString descr)
380{
381 custom_levels.zap();
382
383 // Parse the filter line into individual rules
384 WvStringList lst;
385 WvStringList::Iter i(lst);
386 lst.split(descr, ",= ");
387 if (lst.isempty())
388 return true;
389 WvString src("");
390
391 for (i.rewind(); i.next(); )
392 {
393 if (src != "")
394 {
395 if (atoi(*i) > 0 && atoi(*i) <= WvLog::NUM_LOGLEVELS)
396 {
397 custom_levels.add(new Src_Lvl(src, atoi(*i)), true);
398 src = "";
399 }
400 else
401 return false;
402 }
403 else
404 {
405 src = *i;
406 strlwr(trim_string(src.edit()));
407 }
408 }
409 if (src != "")
410 return false;
411
412 return true;
413}
414
415
417
418
419
420WvLogConsole::WvLogConsole(int _fd, WvLog::LogLevel _max_level) :
421 WvFDStream(_fd), WvLogRcv(_max_level)
422{
423}
424
425
426WvLogConsole::~WvLogConsole()
427{
428 end_line();
429}
430
431
432void WvLogConsole::_mid_line(const char *str, size_t len)
433{
434 uwrite(str, len);
435}
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:94
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
Base class for streams built on Unix file descriptors.
Definition: wvfdstream.h:21
virtual size_t uwrite(const void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by write().
Definition: wvfdstream.cc:162
Captures formatted log messages and outputs them to the specified file descriptor.
Definition: wvlogrcv.h:108
virtual void _mid_line(const char *str, size_t len)
add text to the current log line.
Definition: wvlog.cc:432
WvLogRcv adds some intelligence to WvLogRcvBase, to keep track of line-prefix-printing and other form...
Definition: wvlogrcv.h:29
virtual void _end_line()
End this (Guaranteed NonEmpty) log line.
Definition: wvlog.cc:254
virtual void _make_prefix(time_t now)
Set the Prefix and Prefix Length (size_t prelen)
Definition: wvlog.cc:240
virtual void _begin_line()
Start a new log line (print prefix)
Definition: wvlog.cc:248
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:57
virtual bool isok() const
fd==-1, but this stream is always ok
Definition: wvlog.cc:75
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvlog.cc:91
virtual size_t uwrite(const void *buf, size_t len)
we override the unbuffered write function, so lines also include the application and log level.
Definition: wvlog.cc:101
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvlog.cc:81
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvstream.cc:875
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvstream.cc:844
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:28
void split(WvStringParm s, const char *splitchars=" \t\r\n", int limit=0)
split s and form a list ignoring splitchars (except at beginning and end) ie.
Definition: wvstringlist.cc:19
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:330
the data structure used by pre_select()/post_select() and internally by select().
Definition: iwvstream.h:50
Provides support for forking processes.
void add_wvfork_callback(WvForkCallback cb)
Register a callback to be called during wvfork.
Definition: wvfork.cc:51
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition: strutils.cc:59
char * strlwr(char *string)
In-place modify a character string so that all contained letters are in lower case.
Definition: strutils.cc:201