]> mj.ucw.cz Git - libucw.git/blob - ucw/log.c
8c53d0f5e039215dce05cc869087098fbcc82e1a
[libucw.git] / ucw / log.c
1 /*
2  *      UCW Library -- Logging
3  *
4  *      (c) 1997--2009 Martin Mares <mj@ucw.cz>
5  *      (c) 2008 Tomas Gavenciak <gavento@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "ucw/lib.h"
12 #include "ucw/log.h"
13 #include "ucw/simple-lists.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <sys/time.h>
20 #include <time.h>
21 #include <alloca.h>
22 #include <errno.h>
23
24 char *log_title;
25 int log_pid;
26 void (*log_die_hook)(void);
27
28 /*** The default log stream, which logs to stderr ***/
29
30 static int default_log_handler(struct log_stream *ls, const char *m, uns cat UNUSED)
31 {
32   // This is a completely bare version of the log-file module. Errors are ignored.
33   write(ls->idata, m, strlen(m));
34   return 0;
35 }
36
37 const struct log_stream log_stream_default = {
38   .name = "stderr",
39   .idata = 2,
40   .handler = default_log_handler,
41   .levels = LS_ALL_LEVELS,
42   .msgfmt = LSFMT_DEFAULT,
43   // an empty clist
44   .substreams.head.next = (cnode *) &log_stream_default.substreams.head,
45   .substreams.head.prev = (cnode *) &log_stream_default.substreams.head,
46 };
47
48 /*** Registry of streams and their identifiers ***/
49
50 struct lsbuf_t log_streams;             /* A growing array of pointers to log_streams */
51 int log_streams_after = 0;              /* The first never-used index in log_streams.ptr */
52
53 /*
54  *  Find a stream by its identifier given as LS_SET_STRNUM(flags).
55  *  Returns NULL if the stream doesn't exist or it's invalid.
56  *  When stream #0 is requested, fall back to log_stream_default.
57  */
58
59 struct log_stream *
60 log_stream_by_flags(uns flags)
61 {
62   int n = LS_GET_STRNUM(flags);
63   if (n < 0 || n >= log_streams_after || log_streams.ptr[n]->regnum == -1)
64     return (n ? NULL : (struct log_stream *) &log_stream_default);
65   return log_streams.ptr[n];
66 }
67
68 /*** Logging ***/
69
70 void
71 vmsg(uns cat, const char *fmt, va_list args)
72 {
73   struct timeval tv;
74   int have_tm = 0;
75   struct tm tm;
76   va_list args2;
77   char stime[24];
78   char sutime[12];
79   char msgbuf[256];
80   char *m, *p;
81   int len;
82   struct log_stream *ls = log_stream_by_flags(cat);
83
84   /* Check the stream existence */
85   if (!ls)
86     {
87       msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.", LS_GET_STRNUM(cat));
88       ls = (struct log_stream *) &log_stream_default;
89     }
90
91   /* Get the current time */
92   if (!(cat & LSFLAG_SIGHANDLER))
93     {
94       /* CAVEAT: These calls are not safe in signal handlers. */
95       gettimeofday(&tv, NULL);
96       if (localtime_r(&tv.tv_sec, &tm))
97         have_tm = 1;
98     }
99
100   /* Generate time strings */
101   if (have_tm)
102     {
103       strftime(stime, sizeof(stime), "%Y-%m-%d %H:%M:%S", &tm);
104       snprintf(sutime, sizeof(sutime), ".%06d", (int)tv.tv_usec);
105     }
106   else
107     {
108       snprintf(stime, sizeof(stime), "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?");
109       snprintf(sutime, sizeof(sutime), ".\?\?\?\?\?\?");
110     }
111
112   /* Generate the message string */
113   va_copy(args2, args);
114   len = vsnprintf(msgbuf, sizeof(msgbuf), fmt, args2);
115   va_end(args2);
116   if (len < (int) sizeof(msgbuf))
117     m = msgbuf;
118   else
119     {
120       m = xmalloc(len+1);
121       vsnprintf(m, len+1, fmt, args);
122     }
123
124   /* Remove non-printable characters and newlines */
125   p = m;
126   while (*p)
127     {
128       if (*p < 0x20 && *p != '\t')
129         *p = 0x7f;
130       p++;
131     }
132
133   /* Pass the message to the log_stream */
134   if (log_pass_msg(0, ls, stime, sutime, m, cat))
135     {
136       /* Error (such as infinite loop) occurred */
137       log_pass_msg(0, (struct log_stream *) &log_stream_default, stime, sutime, m, cat);
138     }
139
140   if (m != msgbuf)
141     xfree(m);
142 }
143
144 int
145 log_pass_msg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, uns cat)
146 {
147   ASSERT(ls);
148
149   /* Check recursion depth */
150   if (depth > LS_MAX_DEPTH)
151     {
152       log_pass_msg(0, (struct log_stream *) &log_stream_default, stime, sutime,
153         "Loop in the log_stream system detected.", L_ERROR | (cat & LS_INTERNAL_MASK));
154       return 1;
155     }
156
157   /* Filter by level and hook function */
158   if (!((1 << LS_GET_LEVEL(cat)) & ls->levels))
159     return 0;
160   if (ls->filter && ls->filter(ls, m, cat))
161     return 0;
162
163   /* Pass the message to substreams */
164   CLIST_FOR_EACH(simp_node *, s, ls->substreams)
165     if (log_pass_msg(depth+1, s->p, stime, sutime, m, cat))
166       return 1;
167
168   /* Will pass to the handler of this stream... is there any? */
169   if (!ls->handler)
170     return 0;
171
172   /* Upper bound on message length */
173   int len = strlen(m) + strlen(stime) + strlen(sutime) + 32;
174   if (log_title)
175     len += strlen(log_title);
176   if (ls->name)
177     len += strlen(ls->name);
178
179   /* Get a buffer and format the message */
180   char *free_buf = NULL;
181   char *buf;
182   if (len <= 256)
183     buf = alloca(len);
184   else
185     buf = free_buf = xmalloc(len);
186   char *p = buf;
187
188   /* Level (2 chars) */
189   if (ls->msgfmt & LSFMT_LEVEL)
190     {
191       *p++ = LS_LEVEL_LETTER(LS_GET_LEVEL(cat));
192       *p++ = ' ';
193     }
194
195   /* Time (|stime| + |sutime| + 1 chars) */
196   if (ls->msgfmt & LSFMT_TIME)
197     {
198       const char *q = (char *)stime;
199       while (*q)
200         *p++ = *q++;
201       if (ls->msgfmt & LSFMT_USEC)
202         {
203           q = sutime;
204           while (*q)
205             *p++ = *q++;
206         }
207       *p++ = ' ';
208     }
209
210   /* Process name, PID ( |log_title| + 6 + (|PID|<=10) chars ) */
211   if ((ls->msgfmt & LSFMT_TITLE) && log_title)
212     {
213       if (ls->msgfmt & LSFMT_PID)
214         p += sprintf(p, "[%s (%d)] ", log_title, log_pid);
215       else
216         p += sprintf(p, "[%s] ", log_title);
217     }
218   else
219     {
220       if (ls->msgfmt & LSFMT_PID)
221         p += sprintf(p, "[%d] ", log_pid);
222     }
223
224   /* log_stream name ( |ls->name| + 4 chars ) */
225   if (ls->msgfmt & LSFMT_LOGNAME)
226     {
227       if (ls->name)
228         p += sprintf(p, "<%s> ", ls->name);
229       else
230         p += sprintf(p, "<?> ");
231     }
232
233   /* The message itself ( |m| + 1 chars ) */
234     {
235       const char *q = m;
236       while (*q)
237         *p++ = *q++;
238       *p++ = '\n';
239       *p++ = '\0';
240       ls->handler(ls, buf, cat);
241     }
242
243   if (free_buf)
244     xfree(free_buf);
245   return 0;
246 }
247
248 /*** Utility functions ***/
249
250 void
251 msg(unsigned int cat, const char *fmt, ...)
252 {
253   va_list args;
254
255   va_start(args, fmt);
256   vmsg(cat, fmt, args);
257   va_end(args);
258 }
259
260 void
261 die(const char *fmt, ...)
262 {
263   va_list args;
264
265   va_start(args, fmt);
266   vmsg(L_FATAL, fmt, args);
267   va_end(args);
268   if (log_die_hook)
269     log_die_hook();
270 #ifdef DEBUG_DIE_BY_ABORT
271   abort();
272 #else
273   exit(1);
274 #endif
275 }
276
277 void
278 assert_failed(const char *assertion, const char *file, int line)
279 {
280   msg(L_FATAL, "Assertion `%s' failed at %s:%d", assertion, file, line);
281   abort();
282 }
283
284 void
285 assert_failed_noinfo(void)
286 {
287   die("Internal error: Assertion failed.");
288 }
289
290 static const char *
291 log_basename(const char *n)
292 {
293   const char *p = n;
294
295   while (*n)
296     if (*n++ == '/')
297       p = n;
298   return p;
299 }
300
301 void
302 log_init(const char *argv0)
303 {
304   if (argv0)
305     {
306       static char log_progname[32];
307       strncpy(log_progname, log_basename(argv0), sizeof(log_progname)-1);
308       log_progname[sizeof(log_progname)-1] = 0;
309       log_title = log_progname;
310     }
311 }
312
313 #ifdef TEST
314
315 int main(void)
316 {
317   msg(L_INFO, "Brum <%300s>", ":-)");
318   return 0;
319 }
320
321 #endif