logging: fix missing description of global loglevel
[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 #include <ctype.h>
30
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34 #include <time.h>
35 #include <errno.h>
36
37 #include <osmocom/core/talloc.h>
38 #include <osmocom/core/utils.h>
39 #include <osmocom/core/logging.h>
40
41 #include <osmocom/vty/logging.h>        /* for LOGGING_STR. */
42
43 const struct log_info *osmo_log_info;
44
45 static struct log_context log_context;
46 static void *tall_log_ctx = NULL;
47 LLIST_HEAD(osmo_log_target_list);
48
49 #define LOGLEVEL_DEFS   6       /* Number of loglevels.*/
50
51 static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
52         { 0,            "EVERYTHING" },
53         { LOGL_DEBUG,   "DEBUG" },
54         { LOGL_INFO,    "INFO" },
55         { LOGL_NOTICE,  "NOTICE" },
56         { LOGL_ERROR,   "ERROR" },
57         { LOGL_FATAL,   "FATAL" },
58         { 0, NULL },
59 };
60
61 /* You have to keep this in sync with the structure loglevel_strs. */
62 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
63         "Log simply everything",
64         "Log debug messages and higher levels",
65         "Log informational messages and higher levels",
66         "Log noticable messages and higher levels",
67         "Log error messages and higher levels",
68         "Log only fatal messages",
69         NULL,
70 };
71
72 int log_parse_level(const char *lvl)
73 {
74         return get_string_value(loglevel_strs, lvl);
75 }
76
77 const char *log_level_str(unsigned int lvl)
78 {
79         return get_value_string(loglevel_strs, lvl);
80 }
81
82 int log_parse_category(const char *category)
83 {
84         int i;
85
86         for (i = 0; i < osmo_log_info->num_cat; ++i) {
87                 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
88                         return i;
89         }
90
91         return -EINVAL;
92 }
93
94 /*
95  * Parse the category mask.
96  * The format can be this: category1:category2:category3
97  * or category1,2:category2,3:...
98  */
99 void log_parse_category_mask(struct log_target* target, const char *_mask)
100 {
101         int i = 0;
102         char *mask = strdup(_mask);
103         char *category_token = NULL;
104
105         /* Disable everything to enable it afterwards */
106         for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
107                 target->categories[i].enabled = 0;
108
109         category_token = strtok(mask, ":");
110         do {
111                 for (i = 0; i < osmo_log_info->num_cat; ++i) {
112                         char* colon = strstr(category_token, ",");
113                         int length = strlen(category_token);
114
115                         if (colon)
116                             length = colon - category_token;
117
118                         if (strncasecmp(osmo_log_info->cat[i].name,
119                                         category_token, length) == 0) {
120                                 int level = 0;
121
122                                 if (colon)
123                                         level = atoi(colon+1);
124
125                                 target->categories[i].enabled = 1;
126                                 target->categories[i].loglevel = level;
127                         }
128                 }
129         } while ((category_token = strtok(NULL, ":")));
130
131         free(mask);
132 }
133
134 static const char* color(int subsys)
135 {
136         if (subsys < osmo_log_info->num_cat)
137                 return osmo_log_info->cat[subsys].color;
138
139         return NULL;
140 }
141
142 static void _output(struct log_target *target, unsigned int subsys,
143                     unsigned int level, char *file, int line, int cont,
144                     const char *format, va_list ap)
145 {
146         char col[30];
147         char sub[30];
148         char tim[30];
149         char buf[4096];
150         char final[4096];
151
152         /* prepare the data */
153         col[0] = '\0';
154         sub[0] = '\0';
155         tim[0] = '\0';
156         buf[0] = '\0';
157
158         /* are we using color */
159         if (target->use_color) {
160                 const char *c = color(subsys);
161                 if (c) {
162                         snprintf(col, sizeof(col), "%s", color(subsys));
163                         col[sizeof(col)-1] = '\0';
164                 }
165         }
166         vsnprintf(buf, sizeof(buf), format, ap);
167         buf[sizeof(buf)-1] = '\0';
168
169         if (!cont) {
170                 if (target->print_timestamp) {
171                         char *timestr;
172                         time_t tm;
173                         tm = time(NULL);
174                         timestr = ctime(&tm);
175                         timestr[strlen(timestr)-1] = '\0';
176                         snprintf(tim, sizeof(tim), "%s ", timestr);
177                         tim[sizeof(tim)-1] = '\0';
178                 }
179                 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
180                 sub[sizeof(sub)-1] = '\0';
181         }
182
183         snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
184                  target->use_color ? "\033[0;m" : "");
185         final[sizeof(final)-1] = '\0';
186         target->output(target, level, final);
187 }
188
189
190 static void _logp(unsigned int subsys, int level, char *file, int line,
191                   int cont, const char *format, va_list ap)
192 {
193         struct log_target *tar;
194
195         llist_for_each_entry(tar, &osmo_log_target_list, entry) {
196                 struct log_category *category;
197                 int output = 0;
198
199                 category = &tar->categories[subsys];
200                 /* subsystem is not supposed to be logged */
201                 if (!category->enabled)
202                         continue;
203
204                 /* Check the global log level */
205                 if (tar->loglevel != 0 && level < tar->loglevel)
206                         continue;
207
208                 /* Check the category log level */
209                 if (tar->loglevel == 0 && category->loglevel != 0 &&
210                     level < category->loglevel)
211                         continue;
212
213                 /* Apply filters here... if that becomes messy we will
214                  * need to put filters in a list and each filter will
215                  * say stop, continue, output */
216                 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
217                         output = 1;
218                 else if (osmo_log_info->filter_fn)
219                         output = osmo_log_info->filter_fn(&log_context,
220                                                        tar);
221
222                 if (output) {
223                         /* FIXME: copying the va_list is an ugly
224                          * workaround against a bug hidden somewhere in
225                          * _output.  If we do not copy here, the first
226                          * call to _output() will corrupt the va_list
227                          * contents, and any further _output() calls
228                          * with the same va_list will segfault */
229                         va_list bp;
230                         va_copy(bp, ap);
231                         _output(tar, subsys, level, file, line, cont, format, bp);
232                         va_end(bp);
233                 }
234         }
235 }
236
237 void logp(unsigned int subsys, char *file, int line, int cont,
238           const char *format, ...)
239 {
240         va_list ap;
241
242         va_start(ap, format);
243         _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
244         va_end(ap);
245 }
246
247 void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
248 {
249         va_list ap;
250
251         va_start(ap, format);
252         _logp(subsys, level, file, line, cont, format, ap);
253         va_end(ap);
254 }
255
256 void log_add_target(struct log_target *target)
257 {
258         llist_add_tail(&target->entry, &osmo_log_target_list);
259 }
260
261 void log_del_target(struct log_target *target)
262 {
263         llist_del(&target->entry);
264 }
265
266 void log_reset_context(void)
267 {
268         memset(&log_context, 0, sizeof(log_context));
269 }
270
271 int log_set_context(uint8_t ctx_nr, void *value)
272 {
273         if (ctx_nr > LOG_MAX_CTX)
274                 return -EINVAL;
275
276         log_context.ctx[ctx_nr] = value;
277
278         return 0;
279 }
280
281 void log_set_all_filter(struct log_target *target, int all)
282 {
283         if (all)
284                 target->filter_map |= LOG_FILTER_ALL;
285         else
286                 target->filter_map &= ~LOG_FILTER_ALL;
287 }
288
289 void log_set_use_color(struct log_target *target, int use_color)
290 {
291         target->use_color = use_color;
292 }
293
294 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
295 {
296         target->print_timestamp = print_timestamp;
297 }
298
299 void log_set_log_level(struct log_target *target, int log_level)
300 {
301         target->loglevel = log_level;
302 }
303
304 void log_set_category_filter(struct log_target *target, int category,
305                                int enable, int level)
306 {
307         if (category >= osmo_log_info->num_cat)
308                 return;
309         target->categories[category].enabled = !!enable;
310         target->categories[category].loglevel = level;
311 }
312
313 static void _file_output(struct log_target *target, unsigned int level,
314                          const char *log)
315 {
316         fprintf(target->tgt_file.out, "%s", log);
317         fflush(target->tgt_file.out);
318 }
319
320 struct log_target *log_target_create(void)
321 {
322         struct log_target *target;
323         unsigned int i;
324
325         target = talloc_zero(tall_log_ctx, struct log_target);
326         if (!target)
327                 return NULL;
328
329         INIT_LLIST_HEAD(&target->entry);
330
331         /* initialize the per-category enabled/loglevel from defaults */
332         for (i = 0; i < osmo_log_info->num_cat; i++) {
333                 struct log_category *cat = &target->categories[i];
334                 cat->enabled = osmo_log_info->cat[i].enabled;
335                 cat->loglevel = osmo_log_info->cat[i].loglevel;
336         }
337
338         /* global settings */
339         target->use_color = 1;
340         target->print_timestamp = 0;
341
342         /* global log level */
343         target->loglevel = 0;
344         return target;
345 }
346
347 struct log_target *log_target_create_stderr(void)
348 {
349 /* since C89/C99 says stderr is a macro, we can safely do this! */
350 #ifdef stderr
351         struct log_target *target;
352
353         target = log_target_create();
354         if (!target)
355                 return NULL;
356
357         target->type = LOG_TGT_TYPE_STDERR;
358         target->tgt_file.out = stderr;
359         target->output = _file_output;
360         return target;
361 #else
362         return NULL;
363 #endif /* stderr */
364 }
365
366 struct log_target *log_target_create_file(const char *fname)
367 {
368         struct log_target *target;
369
370         target = log_target_create();
371         if (!target)
372                 return NULL;
373
374         target->type = LOG_TGT_TYPE_FILE;
375         target->tgt_file.out = fopen(fname, "a");
376         if (!target->tgt_file.out)
377                 return NULL;
378
379         target->output = _file_output;
380
381         target->tgt_file.fname = talloc_strdup(target, fname);
382
383         return target;
384 }
385
386 struct log_target *log_target_find(int type, const char *fname)
387 {
388         struct log_target *tgt;
389
390         llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
391                 if (tgt->type != type)
392                         continue;
393                 if (tgt->type == LOG_TGT_TYPE_FILE) {
394                         if (!strcmp(fname, tgt->tgt_file.fname))
395                                 return tgt;
396                 } else
397                         return tgt;
398         }
399         return NULL;
400 }
401
402 void log_target_destroy(struct log_target *target)
403 {
404
405         /* just in case, to make sure we don't have any references */
406         log_del_target(target);
407
408         if (target->output == &_file_output) {
409 /* since C89/C99 says stderr is a macro, we can safely do this! */
410 #ifdef stderr
411                 /* don't close stderr */
412                 if (target->tgt_file.out != stderr)
413 #endif
414                 {
415                         fclose(target->tgt_file.out);
416                         target->tgt_file.out = NULL;
417                 }
418         }
419
420         talloc_free(target);
421 }
422
423 /* close and re-open a log file (for log file rotation) */
424 int log_target_file_reopen(struct log_target *target)
425 {
426         fclose(target->tgt_file.out);
427
428         target->tgt_file.out = fopen(target->tgt_file.fname, "a");
429         if (!target->tgt_file.out)
430                 return -errno;
431
432         /* we assume target->output already to be set */
433
434         return 0;
435 }
436
437 /* This generates the logging command string for VTY. */
438 const char *log_vty_command_string(const struct log_info *info)
439 {
440         int len = 0, offset = 0, ret, i, rem;
441         int size = strlen("logging level () ()") + 1;
442         char *str;
443
444         for (i = 0; i < info->num_cat; i++)
445                 size += strlen(info->cat[i].name) + 1;
446
447         for (i = 0; i < LOGLEVEL_DEFS; i++)
448                 size += strlen(loglevel_strs[i].str) + 1;
449
450         rem = size;
451         str = talloc_zero_size(NULL, size);
452         if (!str)
453                 return NULL;
454
455         ret = snprintf(str + offset, rem, "logging level (all|");
456         if (ret < 0)
457                 goto err;
458         OSMO_SNPRINTF_RET(ret, rem, offset, len);
459
460         for (i = 0; i < info->num_cat; i++) {
461                 int j, name_len = strlen(info->cat[i].name)+1;
462                 char name[name_len];
463
464                 for (j = 0; j < name_len; j++)
465                         name[j] = tolower(info->cat[i].name[j]);
466
467                 name[name_len-1] = '\0';
468                 ret = snprintf(str + offset, rem, "%s|", name+1);
469                 if (ret < 0)
470                         goto err;
471                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
472         }
473         offset--;       /* to remove the trailing | */
474         rem++;
475
476         ret = snprintf(str + offset, rem, ") (");
477         if (ret < 0)
478                 goto err;
479         OSMO_SNPRINTF_RET(ret, rem, offset, len);
480
481         for (i = 0; i < LOGLEVEL_DEFS; i++) {
482                 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
483                 char loglevel_str[loglevel_str_len];
484
485                 for (j = 0; j < loglevel_str_len; j++)
486                         loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
487
488                 loglevel_str[loglevel_str_len-1] = '\0';
489                 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
490                 if (ret < 0)
491                         goto err;
492                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
493         }
494         offset--;       /* to remove the trailing | */
495         rem++;
496
497         ret = snprintf(str + offset, rem, ")");
498         if (ret < 0)
499                 goto err;
500         OSMO_SNPRINTF_RET(ret, rem, offset, len);
501 err:
502         return str;
503 }
504
505 /* This generates the logging command description for VTY. */
506 const char *log_vty_command_description(const struct log_info *info)
507 {
508         char *str;
509         int i, ret, len = 0, offset = 0, rem;
510         unsigned int size =
511                 strlen(LOGGING_STR
512                        "Set the log level for a specified category\n") + 1;
513
514         for (i = 0; i < info->num_cat; i++)
515                 size += strlen(info->cat[i].description) + 1;
516
517         for (i = 0; i < LOGLEVEL_DEFS; i++)
518                 size += strlen(loglevel_descriptions[i]) + 1;
519
520         size += strlen("Global setting for all subsystems") + 1;
521         rem = size;
522         str = talloc_zero_size(NULL, size);
523         if (!str)
524                 return NULL;
525
526         ret = snprintf(str + offset, rem, LOGGING_STR
527                         "Set the log level for a specified category\n");
528         if (ret < 0)
529                 goto err;
530         OSMO_SNPRINTF_RET(ret, rem, offset, len);
531
532         ret = snprintf(str + offset, rem,
533                         "Global setting for all subsystems\n");
534         if (ret < 0)
535                 goto err;
536         OSMO_SNPRINTF_RET(ret, rem, offset, len);
537
538         for (i = 0; i < info->num_cat; i++) {
539                 ret = snprintf(str + offset, rem, "%s\n",
540                                 info->cat[i].description);
541                 if (ret < 0)
542                         goto err;
543                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
544         }
545         for (i = 0; i < LOGLEVEL_DEFS; i++) {
546                 ret = snprintf(str + offset, rem, "%s\n",
547                                 loglevel_descriptions[i]);
548                 if (ret < 0)
549                         goto err;
550                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
551         }
552 err:
553         return str;
554 }
555
556 void log_init(const struct log_info *cat)
557 {
558         tall_log_ctx = talloc_named_const(NULL, 1, "logging");
559         osmo_log_info = cat;
560 }