LOGGING: Pass the log level down to the log target output function
[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                     unsigned int level, char *file, int line, int cont,
128                     const char *format, 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, level, 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, level, 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, unsigned int level,
298                          const char *log)
299 {
300         fprintf(target->tgt_file.out, "%s", log);
301         fflush(target->tgt_file.out);
302 }
303
304 struct log_target *log_target_create(void)
305 {
306         struct log_target *target;
307         unsigned int i;
308
309         target = talloc_zero(tall_log_ctx, struct log_target);
310         if (!target)
311                 return NULL;
312
313         INIT_LLIST_HEAD(&target->entry);
314
315         /* initialize the per-category enabled/loglevel from defaults */
316         for (i = 0; i < osmo_log_info->num_cat; i++) {
317                 struct log_category *cat = &target->categories[i];
318                 cat->enabled = osmo_log_info->cat[i].enabled;
319                 cat->loglevel = osmo_log_info->cat[i].loglevel;
320         }
321
322         /* global settings */
323         target->use_color = 1;
324         target->print_timestamp = 0;
325
326         /* global log level */
327         target->loglevel = 0;
328         return target;
329 }
330
331 struct log_target *log_target_create_stderr(void)
332 {
333 /* since C89/C99 says stderr is a macro, we can safely do this! */
334 #ifdef stderr
335         struct log_target *target;
336
337         target = log_target_create();
338         if (!target)
339                 return NULL;
340
341         target->tgt_file.out = stderr;
342         target->output = _file_output;
343         return target;
344 #else
345         return NULL;
346 #endif /* stderr */
347 }
348
349 struct log_target *log_target_create_file(const char *fname)
350 {
351         struct log_target *target;
352
353         target = log_target_create();
354         if (!target)
355                 return NULL;
356
357         target->tgt_file.out = fopen(fname, "a");
358         if (!target->tgt_file.out)
359                 return NULL;
360
361         target->output = _file_output;
362
363         target->tgt_file.fname = talloc_strdup(target, fname);
364
365         return target;
366 }
367
368 void log_target_destroy(struct log_target *target)
369 {
370
371         /* just in case, to make sure we don't have any references */
372         log_del_target(target);
373
374         if (target->output == &_file_output) {
375 /* since C89/C99 says stderr is a macro, we can safely do this! */
376 #ifdef stderr
377                 /* don't close stderr */
378                 if (target->tgt_file.out != stderr)
379 #endif
380                 {
381                         fclose(target->tgt_file.out);
382                         target->tgt_file.out = NULL;
383                 }
384         }
385
386         talloc_free(target);
387 }
388
389 /* close and re-open a log file (for log file rotation) */
390 int log_target_file_reopen(struct log_target *target)
391 {
392         fclose(target->tgt_file.out);
393
394         target->tgt_file.out = fopen(target->tgt_file.fname, "a");
395         if (!target->tgt_file.out)
396                 return -errno;
397
398         /* we assume target->output already to be set */
399
400         return 0;
401 }
402
403 const char *log_vty_level_string(struct log_info *info)
404 {
405         const struct value_string *vs;
406         unsigned int len = 3; /* ()\0 */
407         char *str;
408
409         for (vs = loglevel_strs; vs->value || vs->str; vs++)
410                 len += strlen(vs->str) + 1;
411
412         str = talloc_zero_size(NULL, len);
413         if (!str)
414                 return NULL;
415
416         str[0] = '(';
417         for (vs = loglevel_strs; vs->value || vs->str; vs++) {
418                 strcat(str, vs->str);
419                 strcat(str, "|");
420         }
421         str[strlen(str)-1] = ')';
422
423         return str;
424 }
425
426 const char *log_vty_category_string(struct log_info *info)
427 {
428         unsigned int len = 3;   /* "()\0" */
429         unsigned int i;
430         char *str;
431
432         for (i = 0; i < info->num_cat; i++)
433                 len += strlen(info->cat[i].name) + 1;
434
435         str = talloc_zero_size(NULL, len);
436         if (!str)
437                 return NULL;
438
439         str[0] = '(';
440         for (i = 0; i < info->num_cat; i++) {
441                 strcat(str, info->cat[i].name+1);
442                 strcat(str, "|");
443         }
444         str[strlen(str)-1] = ')';
445
446         return str;
447 }
448
449 void log_init(const struct log_info *cat)
450 {
451         tall_log_ctx = talloc_named_const(NULL, 1, "logging");
452         osmo_log_info = cat;
453 }