b3b5cb69e5d0dde4260fdf874d1929326d6fa79d
[osmocom-bb.git] / src / logging.c
1 /* Debugging/Logging support code */
2
3 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4  * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include "../config.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #ifdef HAVE_STRINGS_H
31 #include <strings.h>
32 #endif
33 #include <time.h>
34 #include <errno.h>
35
36 #include <osmocore/talloc.h>
37 #include <osmocore/utils.h>
38 #include <osmocore/logging.h>
39
40 const struct log_info *osmo_log_info;
41
42 static struct log_context log_context;
43 static void *tall_log_ctx = NULL;
44 static LLIST_HEAD(target_list);
45
46 static const struct value_string loglevel_strs[] = {
47         { 0,            "EVERYTHING" },
48         { LOGL_DEBUG,   "DEBUG" },
49         { LOGL_INFO,    "INFO" },
50         { LOGL_NOTICE,  "NOTICE" },
51         { LOGL_ERROR,   "ERROR" },
52         { LOGL_FATAL,   "FATAL" },
53         { 0, NULL },
54 };
55
56 int log_parse_level(const char *lvl)
57 {
58         return get_string_value(loglevel_strs, lvl);
59 }
60
61 const char *log_level_str(unsigned int lvl)
62 {
63         return get_value_string(loglevel_strs, lvl);
64 }
65
66 int log_parse_category(const char *category)
67 {
68         int i;
69
70         for (i = 0; i < osmo_log_info->num_cat; ++i) {
71                 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
72                         return i;
73         }
74
75         return -EINVAL;
76 }
77
78 /*
79  * Parse the category mask.
80  * The format can be this: category1:category2:category3
81  * or category1,2:category2,3:...
82  */
83 void log_parse_category_mask(struct log_target* target, const char *_mask)
84 {
85         int i = 0;
86         char *mask = strdup(_mask);
87         char *category_token = NULL;
88
89         /* Disable everything to enable it afterwards */
90         for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
91                 target->categories[i].enabled = 0;
92
93         category_token = strtok(mask, ":");
94         do {
95                 for (i = 0; i < osmo_log_info->num_cat; ++i) {
96                         char* colon = strstr(category_token, ",");
97                         int length = strlen(category_token);
98
99                         if (colon)
100                             length = colon - category_token;
101
102                         if (strncasecmp(osmo_log_info->cat[i].name,
103                                         category_token, length) == 0) {
104                                 int level = 0;
105
106                                 if (colon)
107                                         level = atoi(colon+1);
108
109                                 target->categories[i].enabled = 1;
110                                 target->categories[i].loglevel = level;
111                         }
112                 }
113         } while ((category_token = strtok(NULL, ":")));
114
115         free(mask);
116 }
117
118 static const char* color(int subsys)
119 {
120         if (subsys < osmo_log_info->num_cat)
121                 return osmo_log_info->cat[subsys].color;
122
123         return NULL;
124 }
125
126 static void _output(struct log_target *target, unsigned int subsys,
127                     char *file, int line, int cont, const char *format,
128                     va_list ap)
129 {
130         char col[30];
131         char sub[30];
132         char tim[30];
133         char buf[4096];
134         char final[4096];
135
136         /* prepare the data */
137         col[0] = '\0';
138         sub[0] = '\0';
139         tim[0] = '\0';
140         buf[0] = '\0';
141
142         /* are we using color */
143         if (target->use_color) {
144                 const char *c = color(subsys);
145                 if (c) {
146                         snprintf(col, sizeof(col), "%s", color(subsys));
147                         col[sizeof(col)-1] = '\0';
148                 }
149         }
150         vsnprintf(buf, sizeof(buf), format, ap);
151         buf[sizeof(buf)-1] = '\0';
152
153         if (!cont) {
154                 if (target->print_timestamp) {
155                         char *timestr;
156                         time_t tm;
157                         tm = time(NULL);
158                         timestr = ctime(&tm);
159                         timestr[strlen(timestr)-1] = '\0';
160                         snprintf(tim, sizeof(tim), "%s ", timestr);
161                         tim[sizeof(tim)-1] = '\0';
162                 }
163                 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
164                 sub[sizeof(sub)-1] = '\0';
165         }
166
167         snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
168                  target->use_color ? "\033[0;m" : "");
169         final[sizeof(final)-1] = '\0';
170         target->output(target, final);
171 }
172
173
174 static void _logp(unsigned int subsys, int level, char *file, int line,
175                   int cont, const char *format, va_list ap)
176 {
177         struct log_target *tar;
178
179         llist_for_each_entry(tar, &target_list, entry) {
180                 struct log_category *category;
181                 int output = 0;
182
183                 category = &tar->categories[subsys];
184                 /* subsystem is not supposed to be logged */
185                 if (!category->enabled)
186                         continue;
187
188                 /* Check the global log level */
189                 if (tar->loglevel != 0 && level < tar->loglevel)
190                         continue;
191
192                 /* Check the category log level */
193                 if (tar->loglevel == 0 && category->loglevel != 0 &&
194                     level < category->loglevel)
195                         continue;
196
197                 /* Apply filters here... if that becomes messy we will
198                  * need to put filters in a list and each filter will
199                  * say stop, continue, output */
200                 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
201                         output = 1;
202                 else if (osmo_log_info->filter_fn)
203                         output = osmo_log_info->filter_fn(&log_context,
204                                                        tar);
205
206                 if (output) {
207                         /* FIXME: copying the va_list is an ugly
208                          * workaround against a bug hidden somewhere in
209                          * _output.  If we do not copy here, the first
210                          * call to _output() will corrupt the va_list
211                          * contents, and any further _output() calls
212                          * with the same va_list will segfault */
213                         va_list bp;
214                         va_copy(bp, ap);
215                         _output(tar, subsys, file, line, cont, format, bp);
216                         va_end(bp);
217                 }
218         }
219 }
220
221 void logp(unsigned int subsys, char *file, int line, int cont,
222           const char *format, ...)
223 {
224         va_list ap;
225
226         va_start(ap, format);
227         _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
228         va_end(ap);
229 }
230
231 void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
232 {
233         va_list ap;
234
235         va_start(ap, format);
236         _logp(subsys, level, file, line, cont, format, ap);
237         va_end(ap);
238 }
239
240 void log_add_target(struct log_target *target)
241 {
242         llist_add_tail(&target->entry, &target_list);
243 }
244
245 void log_del_target(struct log_target *target)
246 {
247         llist_del(&target->entry);
248 }
249
250 void log_reset_context(void)
251 {
252         memset(&log_context, 0, sizeof(log_context));
253 }
254
255 int log_set_context(uint8_t ctx_nr, void *value)
256 {
257         if (ctx_nr > LOG_MAX_CTX)
258                 return -EINVAL;
259
260         log_context.ctx[ctx_nr] = value;
261
262         return 0;
263 }
264
265 void log_set_all_filter(struct log_target *target, int all)
266 {
267         if (all)
268                 target->filter_map |= LOG_FILTER_ALL;
269         else
270                 target->filter_map &= ~LOG_FILTER_ALL;
271 }
272
273 void log_set_use_color(struct log_target *target, int use_color)
274 {
275         target->use_color = use_color;
276 }
277
278 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
279 {
280         target->print_timestamp = print_timestamp;
281 }
282
283 void log_set_log_level(struct log_target *target, int log_level)
284 {
285         target->loglevel = log_level;
286 }
287
288 void log_set_category_filter(struct log_target *target, int category,
289                                int enable, int level)
290 {
291         if (category >= osmo_log_info->num_cat)
292                 return;
293         target->categories[category].enabled = !!enable;
294         target->categories[category].loglevel = level;
295 }
296
297 static void _file_output(struct log_target *target, const char *log)
298 {
299         fprintf(target->tgt_file.out, "%s", log);
300         fflush(target->tgt_file.out);
301 }
302
303 struct log_target *log_target_create(void)
304 {
305         struct log_target *target;
306         unsigned int i;
307
308         target = talloc_zero(tall_log_ctx, struct log_target);
309         if (!target)
310                 return NULL;
311
312         INIT_LLIST_HEAD(&target->entry);
313
314         /* initialize the per-category enabled/loglevel from defaults */
315         for (i = 0; i < osmo_log_info->num_cat; i++) {
316                 struct log_category *cat = &target->categories[i];
317                 cat->enabled = osmo_log_info->cat[i].enabled;
318                 cat->loglevel = osmo_log_info->cat[i].loglevel;
319         }
320
321         /* global settings */
322         target->use_color = 1;
323         target->print_timestamp = 0;
324
325         /* global log level */
326         target->loglevel = 0;
327         return target;
328 }
329
330 struct log_target *log_target_create_stderr(void)
331 {
332 /* since C89/C99 says stderr is a macro, we can safely do this! */
333 #ifdef stderr
334         struct log_target *target;
335
336         target = log_target_create();
337         if (!target)
338                 return NULL;
339
340         target->tgt_file.out = stderr;
341         target->output = _file_output;
342         return target;
343 #else
344         return NULL;
345 #endif /* stderr */
346 }
347
348 struct log_target *log_target_create_file(const char *fname)
349 {
350         struct log_target *target;
351
352         target = log_target_create();
353         if (!target)
354                 return NULL;
355
356         target->tgt_file.out = fopen(fname, "a");
357         if (!target->tgt_file.out)
358                 return NULL;
359
360         target->output = _file_output;
361
362         target->tgt_file.fname = talloc_strdup(target, fname);
363
364         return target;
365 }
366
367 void log_target_destroy(struct log_target *target)
368 {
369
370         /* just in case, to make sure we don't have any references */
371         log_del_target(target);
372
373         if (target->output == &_file_output) {
374 /* since C89/C99 says stderr is a macro, we can safely do this! */
375 #ifdef stderr
376                 /* don't close stderr */
377                 if (target->tgt_file.out != stderr)
378 #endif
379                 {
380                         fclose(target->tgt_file.out);
381                         target->tgt_file.out = NULL;
382                 }
383         }
384
385         talloc_free(target);
386 }
387
388 /* close and re-open a log file (for log file rotation) */
389 int log_target_file_reopen(struct log_target *target)
390 {
391         fclose(target->tgt_file.out);
392
393         target->tgt_file.out = fopen(target->tgt_file.fname, "a");
394         if (!target->tgt_file.out)
395                 return -errno;
396
397         /* we assume target->output already to be set */
398
399         return 0;
400 }
401
402 const char *log_vty_level_string(struct log_info *info)
403 {
404         const struct value_string *vs;
405         unsigned int len = 3; /* ()\0 */
406         char *str;
407
408         for (vs = loglevel_strs; vs->value || vs->str; vs++)
409                 len += strlen(vs->str) + 1;
410
411         str = talloc_zero_size(NULL, len);
412         if (!str)
413                 return NULL;
414
415         str[0] = '(';
416         for (vs = loglevel_strs; vs->value || vs->str; vs++) {
417                 strcat(str, vs->str);
418                 strcat(str, "|");
419         }
420         str[strlen(str)-1] = ')';
421
422         return str;
423 }
424
425 const char *log_vty_category_string(struct log_info *info)
426 {
427         unsigned int len = 3;   /* "()\0" */
428         unsigned int i;
429         char *str;
430
431         for (i = 0; i < info->num_cat; i++)
432                 len += strlen(info->cat[i].name) + 1;
433
434         str = talloc_zero_size(NULL, len);
435         if (!str)
436                 return NULL;
437
438         str[0] = '(';
439         for (i = 0; i < info->num_cat; i++) {
440                 strcat(str, info->cat[i].name+1);
441                 strcat(str, "|");
442         }
443         str[strlen(str)-1] = ')';
444
445         return str;
446 }
447
448 void log_init(const struct log_info *cat)
449 {
450         tall_log_ctx = talloc_named_const(NULL, 1, "logging");
451         osmo_log_info = cat;
452 }