]> mj.ucw.cz Git - libucw.git/blob - ucw/log.c
44be63023fe36da9964725f0e14bec6d737cd711
[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 static char log_progname[32];
25 char *log_title;
26 int log_pid;
27 void (*log_die_hook)(void);
28
29 // FIXME: duplicate?
30 static int ls_default_handler(struct log_stream* ls, const char *m, u32 cat UNUSED)
31 {
32   int len = strlen(m);
33   int r = write(ls->idata, m, len);
34   /* TODO: check the errors here? */
35   if (r!=len)
36     return errno;
37   return 0;
38 }
39
40 /* the default logger */
41 const struct log_stream ls_default_log={
42   .name = "fd2", .idata = 2, .pdata = NULL, .regnum = 0,
43   .handler = ls_default_handler,
44   .levels = LS_ALL_LEVELS,
45   .msgfmt = LSFMT_DEFAULT,
46   // empty clist
47   .substreams.head.next = (cnode *) &ls_default_log.substreams.head,
48   .substreams.head.prev = (cnode *) &ls_default_log.substreams.head,
49 };
50
51 /*** Registry of streams and their identifiers ***/
52
53 /* The growing array of pointers to log_streams. */
54 struct lsbuf_t log_streams;
55
56 /* the first never-used index in ls_streams.ptr */
57 int log_streams_after = 0;
58
59 /* get a stream by its LS_SET_STRNUM() */
60 /* returns NULL for free/invalid numbers */
61 /* defaults to ls_default_stream when stream number 0 closed */
62 struct log_stream *log_stream_by_flags(uns flags)
63 {
64   /* get the real number */
65   int n = LS_GET_STRNUM(flags);
66   if ((n<0) || (n>=log_streams_after) || (log_streams.ptr[n]->regnum==-1) )
67   {
68     if (n==0)
69       return (struct log_stream *)&ls_default_log;
70     else
71       return NULL;
72   }
73   return log_streams.ptr[n];
74 }
75
76 /*** Logging ***/
77
78 void vmsg(unsigned int cat, const char *fmt, va_list args)
79 {
80   struct timeval tv;
81   int have_tm = 0;
82   struct tm tm;
83   va_list args2;
84   char stime[24];
85   char sutime[12];
86   char *buf,*p;
87   int len;
88   struct log_stream *ls=log_stream_by_flags(cat);
89
90   /* Check the stream existence */
91   if(!ls)
92     {
93       msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.",
94         LS_GET_STRNUM(cat));
95       ls=(struct log_stream *)&ls_default_log;
96     }
97
98   /* get the time */
99   if (!(cat&LSFLAG_SIGHANDLER))
100     {
101       /* CAVEAT: These calls are not safe in signal handlers. */
102       gettimeofday(&tv, NULL);
103       if (localtime_r(&tv.tv_sec, &tm))
104         have_tm = 1;
105     }
106
107   /* generate time strings */
108   if (have_tm)
109     {
110       strftime(stime, 24, "%Y-%m-%d %H:%M:%S", &tm);
111       snprintf(sutime, 12, ".%06d", (int)tv.tv_usec);
112     }
113   else
114     {
115       snprintf(stime, 24, "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?");
116       snprintf(sutime, 12, ".\?\?\?\?\?\?");
117     }
118
119   /* generate the message string */
120   va_copy(args2, args);
121   /* WARN: this may be C99 specefic */
122   len = vsnprintf(NULL, 0, fmt, args2);
123   va_end(args2);
124   buf = xmalloc(len+2);
125   vsnprintf(buf, len+1, fmt, args);
126
127   /* remove non-printable characters and newlines */
128   p=buf;
129   while (*p)
130     {
131       if (*p < 0x20 && *p != '\t')
132         *p = 0x7f;
133       p++;
134     }
135
136   /* pass the message to the log_stream */
137   if(ls_passmsg(0, ls, stime, sutime, buf, cat))
138     {
139       /* error (such as looping) occured */
140       ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, buf, cat);
141     }
142
143   xfree(buf);
144 }
145
146 /* process a message (string) */
147 /* depth prevents undetected looping */
148 /* returns 1 in case of loop detection or other fatal error
149  *         0 otherwise */
150 int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, u32 cat)
151 {
152   ASSERT(ls);
153
154   /* Check recursion depth */
155   if( depth > LS_MAX_DEPTH )
156     {
157       ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime,
158         "Loop in the log_stream system detected.", L_ERROR | (cat&LS_INTERNAL_MASK) );
159       return 1;
160     }
161
162   /* Filter by level and filter hook */
163   if(!( (1<<LS_GET_LEVEL(cat)) & ls->levels )) return 0;
164   if( ls->filter )
165     if( ls->filter(ls, m, cat) != 0 ) return 0;
166
167   /* pass message to substreams */
168   CLIST_FOR_EACH(simp_node *, s, ls->substreams)
169     {
170       if (ls_passmsg(depth+1, (struct log_stream*)(s->p), stime, sutime, m, cat))
171         return 1;
172     }
173
174   /* Prepare for handler */
175   if(ls->handler)
176     {
177       int len = strlen(m) + strlen(stime) + strlen(sutime) + 32;
178       /* SHOULD be enough for all information, but beware */
179       if (log_title)  len += strlen(log_title);
180       if (ls->name)  len += strlen(ls->name);
181       char *buf=xmalloc(len);
182       char *p=buf;
183
184       /* Level (2 chars) */
185       if(ls->msgfmt & LSFMT_LEVEL)
186         {
187           *p++=LS_LEVEL_LETTER(LS_GET_LEVEL(cat));
188           *p++=' ';
189         }
190
191       /* Time (|stime| + |sutime| + 1 chars) */
192       if(ls->msgfmt & LSFMT_TIME)
193         {
194           char *q = (char *)stime;
195
196           while(*q)
197             *p++=*q++;
198           if(ls->msgfmt & LSFMT_USEC)
199             {
200               q = (char *)sutime;
201               while(*q)
202                 *p++=*q++;
203             }
204           *p++=' ';
205         }
206
207       /* process name, PID ( |log_title| + 6 + (|PID|<=10) chars ) */
208       if((ls->msgfmt & LSFMT_TITLE) && log_title)
209         {
210           if(ls->msgfmt & LSFMT_PID)
211             p += sprintf(p, "[%s (%d)] ", log_title, getpid());
212           else
213             p += sprintf(p, "[%s] ", log_title);
214         }
215       else
216         {
217           if(ls->msgfmt & LSFMT_PID)
218             p += sprintf(p, "[%d] ", getpid());
219         }
220
221       /* log_stream name ( |ls->name| + 4 chars ) */
222       if(ls->msgfmt & LSFMT_LOGNAME)
223         {
224           if(ls->name)
225             p += sprintf(p, "<%s> ", ls->name);
226           else
227             p += sprintf(p, "<?> ");
228         }
229
230       /* finish the string and call the handler ( |m| + 1 chars ) */
231         {
232           char *q = (char *)m;
233
234           while(*q)
235             *p++=*q++;
236           *p++ = '\n';
237           *p++ = '\0';
238           ls->handler(ls, buf, cat);
239         }
240       xfree(buf);
241     }
242   return 0;
243 }
244
245 void
246 msg(unsigned int cat, const char *fmt, ...)
247 {
248   va_list args;
249
250   va_start(args, fmt);
251   vmsg(cat, fmt, args);
252   va_end(args);
253 }
254
255 void
256 die(const char *fmt, ...)
257 {
258   va_list args;
259
260   va_start(args, fmt);
261   vmsg(L_FATAL, fmt, args);
262   va_end(args);
263   if (log_die_hook)
264     log_die_hook();
265 #ifdef DEBUG_DIE_BY_ABORT
266   abort();
267 #else
268   exit(1);
269 #endif
270 }
271
272 void
273 assert_failed(const char *assertion, const char *file, int line)
274 {
275   msg(L_FATAL, "Assertion `%s' failed at %s:%d", assertion, file, line);
276   abort();
277 }
278
279 void
280 assert_failed_noinfo(void)
281 {
282   die("Internal error: Assertion failed.");
283 }
284
285 static const char *
286 log_basename(const char *n)
287 {
288   const char *p = n;
289
290   while (*n)
291     if (*n++ == '/')
292       p = n;
293   return p;
294 }
295
296 void
297 log_init(const char *argv0)
298 {
299   if (argv0)
300     {
301       strncpy(log_progname, log_basename(argv0), sizeof(log_progname)-1);
302       log_progname[sizeof(log_progname)-1] = 0;
303       log_title = log_progname;
304     }
305 }
306
307 #ifdef TEST
308
309 int main(void)
310 {
311   // ls_default_log.msgfmt |= LSFMT_USEC;
312   msg(L_INFO, "Brum!");
313   return 0;
314 }
315
316 #endif