1 /* Debugging/Logging support code */
3 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
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.
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.
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.
23 #include "../config.h"
37 #include <osmocom/core/talloc.h>
38 #include <osmocom/core/utils.h>
39 #include <osmocom/core/logging.h>
41 #include <osmocom/vty/logging.h> /* for LOGGING_STR. */
43 struct log_info *osmo_log_info;
45 static struct log_context log_context;
46 static void *tall_log_ctx = NULL;
47 LLIST_HEAD(osmo_log_target_list);
49 #define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
51 static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
53 { LOGL_DEBUG, "DEBUG" },
54 { LOGL_INFO, "INFO" },
55 { LOGL_NOTICE, "NOTICE" },
56 { LOGL_ERROR, "ERROR" },
57 { LOGL_FATAL, "FATAL" },
61 #define INT2IDX(x) (-1*(x)-1)
62 static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
63 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
65 .description = "Library-internal global log family",
66 .loglevel = LOGL_NOTICE,
69 [INT2IDX(DLLAPDM)] = { /* -2 becomes 1 */
71 .description = "LAPDm in libosmogsm",
72 .loglevel = LOGL_NOTICE,
77 .description = "A-bis Intput Subsystem",
78 .loglevel = LOGL_NOTICE,
83 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
84 .loglevel = LOGL_NOTICE,
89 .description = "A-bis Input Driver for Signalling",
90 .enabled = 0, .loglevel = LOGL_NOTICE,
94 .description = "A-bis Input Driver for B-Channels (voice)",
95 .enabled = 0, .loglevel = LOGL_NOTICE,
99 /* You have to keep this in sync with the structure loglevel_strs. */
100 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
101 "Log simply everything",
102 "Log debug messages and higher levels",
103 "Log informational messages and higher levels",
104 "Log noticable messages and higher levels",
105 "Log error messages and higher levels",
106 "Log only fatal messages",
110 /* special magic for negative (library-internal) log subsystem numbers */
111 static int subsys_lib2index(int subsys)
113 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
116 int log_parse_level(const char *lvl)
118 return get_string_value(loglevel_strs, lvl);
121 const char *log_level_str(unsigned int lvl)
123 return get_value_string(loglevel_strs, lvl);
126 int log_parse_category(const char *category)
130 for (i = 0; i < osmo_log_info->num_cat; ++i) {
131 if (osmo_log_info->cat[i].name == NULL)
133 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
141 * Parse the category mask.
142 * The format can be this: category1:category2:category3
143 * or category1,2:category2,3:...
145 void log_parse_category_mask(struct log_target* target, const char *_mask)
148 char *mask = strdup(_mask);
149 char *category_token = NULL;
151 /* Disable everything to enable it afterwards */
152 for (i = 0; i < osmo_log_info->num_cat; ++i)
153 target->categories[i].enabled = 0;
155 category_token = strtok(mask, ":");
157 for (i = 0; i < osmo_log_info->num_cat; ++i) {
158 char* colon = strstr(category_token, ",");
159 int length = strlen(category_token);
161 if (!osmo_log_info->cat[i].name)
165 length = colon - category_token;
167 if (strncasecmp(osmo_log_info->cat[i].name,
168 category_token, length) == 0) {
172 level = atoi(colon+1);
174 target->categories[i].enabled = 1;
175 target->categories[i].loglevel = level;
178 } while ((category_token = strtok(NULL, ":")));
183 static const char* color(int subsys)
185 if (subsys < osmo_log_info->num_cat)
186 return osmo_log_info->cat[subsys].color;
191 static void _output(struct log_target *target, unsigned int subsys,
192 unsigned int level, char *file, int line, int cont,
193 const char *format, va_list ap)
196 int ret, len = 0, offset = 0, rem = sizeof(buf);
198 /* are we using color */
199 if (target->use_color) {
200 const char *c = color(subsys);
202 ret = snprintf(buf + offset, rem, "%s", color(subsys));
205 OSMO_SNPRINTF_RET(ret, rem, offset, len);
209 if (target->print_timestamp) {
213 timestr = ctime(&tm);
214 timestr[strlen(timestr)-1] = '\0';
215 ret = snprintf(buf + offset, rem, "%s ", timestr);
218 OSMO_SNPRINTF_RET(ret, rem, offset, len);
220 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
224 OSMO_SNPRINTF_RET(ret, rem, offset, len);
226 ret = vsnprintf(buf + offset, rem, format, ap);
229 OSMO_SNPRINTF_RET(ret, rem, offset, len);
231 ret = snprintf(buf + offset, rem, "%s",
232 target->use_color ? "\033[0;m" : "");
235 OSMO_SNPRINTF_RET(ret, rem, offset, len);
237 buf[sizeof(buf)-1] = '\0';
238 target->output(target, level, buf);
241 static void _logp(int subsys, int level, char *file, int line,
242 int cont, const char *format, va_list ap)
244 struct log_target *tar;
247 subsys = subsys_lib2index(subsys);
249 if (subsys > osmo_log_info->num_cat)
252 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
253 struct log_category *category;
257 category = &tar->categories[subsys];
258 /* subsystem is not supposed to be logged */
259 if (!category->enabled)
262 /* Check the global log level */
263 if (tar->loglevel != 0 && level < tar->loglevel)
266 /* Check the category log level */
267 if (tar->loglevel == 0 && category->loglevel != 0 &&
268 level < category->loglevel)
271 /* Apply filters here... if that becomes messy we will
272 * need to put filters in a list and each filter will
273 * say stop, continue, output */
274 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
276 else if (osmo_log_info->filter_fn)
277 output = osmo_log_info->filter_fn(&log_context,
282 /* According to the manpage, vsnprintf leaves the value of ap
283 * in undefined state. Since _output uses vsnprintf and it may
284 * be called several times, we have to pass a copy of ap. */
286 _output(tar, subsys, level, file, line, cont, format, bp);
291 void logp(int subsys, char *file, int line, int cont,
292 const char *format, ...)
296 va_start(ap, format);
297 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
301 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
305 va_start(ap, format);
306 _logp(subsys, level, file, line, cont, format, ap);
310 void log_add_target(struct log_target *target)
312 llist_add_tail(&target->entry, &osmo_log_target_list);
315 void log_del_target(struct log_target *target)
317 llist_del(&target->entry);
320 void log_reset_context(void)
322 memset(&log_context, 0, sizeof(log_context));
325 int log_set_context(uint8_t ctx_nr, void *value)
327 if (ctx_nr > LOG_MAX_CTX)
330 log_context.ctx[ctx_nr] = value;
335 void log_set_all_filter(struct log_target *target, int all)
338 target->filter_map |= LOG_FILTER_ALL;
340 target->filter_map &= ~LOG_FILTER_ALL;
343 void log_set_use_color(struct log_target *target, int use_color)
345 target->use_color = use_color;
348 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
350 target->print_timestamp = print_timestamp;
353 void log_set_log_level(struct log_target *target, int log_level)
355 target->loglevel = log_level;
358 void log_set_category_filter(struct log_target *target, int category,
359 int enable, int level)
361 if (category >= osmo_log_info->num_cat)
363 target->categories[category].enabled = !!enable;
364 target->categories[category].loglevel = level;
367 static void _file_output(struct log_target *target, unsigned int level,
370 fprintf(target->tgt_file.out, "%s", log);
371 fflush(target->tgt_file.out);
374 struct log_target *log_target_create(void)
376 struct log_target *target;
379 target = talloc_zero(tall_log_ctx, struct log_target);
383 target->categories = talloc_zero_array(target,
385 osmo_log_info->num_cat);
386 if (!target->categories) {
391 INIT_LLIST_HEAD(&target->entry);
393 /* initialize the per-category enabled/loglevel from defaults */
394 for (i = 0; i < osmo_log_info->num_cat; i++) {
395 struct log_category *cat = &target->categories[i];
396 cat->enabled = osmo_log_info->cat[i].enabled;
397 cat->loglevel = osmo_log_info->cat[i].loglevel;
400 /* global settings */
401 target->use_color = 1;
402 target->print_timestamp = 0;
404 /* global log level */
405 target->loglevel = 0;
409 struct log_target *log_target_create_stderr(void)
411 /* since C89/C99 says stderr is a macro, we can safely do this! */
413 struct log_target *target;
415 target = log_target_create();
419 target->type = LOG_TGT_TYPE_STDERR;
420 target->tgt_file.out = stderr;
421 target->output = _file_output;
428 struct log_target *log_target_create_file(const char *fname)
430 struct log_target *target;
432 target = log_target_create();
436 target->type = LOG_TGT_TYPE_FILE;
437 target->tgt_file.out = fopen(fname, "a");
438 if (!target->tgt_file.out)
441 target->output = _file_output;
443 target->tgt_file.fname = talloc_strdup(target, fname);
448 struct log_target *log_target_find(int type, const char *fname)
450 struct log_target *tgt;
452 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
453 if (tgt->type != type)
455 if (tgt->type == LOG_TGT_TYPE_FILE) {
456 if (!strcmp(fname, tgt->tgt_file.fname))
464 void log_target_destroy(struct log_target *target)
467 /* just in case, to make sure we don't have any references */
468 log_del_target(target);
470 if (target->output == &_file_output) {
471 /* since C89/C99 says stderr is a macro, we can safely do this! */
473 /* don't close stderr */
474 if (target->tgt_file.out != stderr)
477 fclose(target->tgt_file.out);
478 target->tgt_file.out = NULL;
485 /* close and re-open a log file (for log file rotation) */
486 int log_target_file_reopen(struct log_target *target)
488 fclose(target->tgt_file.out);
490 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
491 if (!target->tgt_file.out)
494 /* we assume target->output already to be set */
499 /* This generates the logging command string for VTY. */
500 const char *log_vty_command_string(const struct log_info *unused_info)
502 struct log_info *info = osmo_log_info;
503 int len = 0, offset = 0, ret, i, rem;
504 int size = strlen("logging level () ()") + 1;
507 for (i = 0; i < info->num_cat; i++) {
508 if (info->cat[i].name == NULL)
510 size += strlen(info->cat[i].name) + 1;
513 for (i = 0; i < LOGLEVEL_DEFS; i++)
514 size += strlen(loglevel_strs[i].str) + 1;
517 str = talloc_zero_size(tall_log_ctx, size);
521 ret = snprintf(str + offset, rem, "logging level (all|");
524 OSMO_SNPRINTF_RET(ret, rem, offset, len);
526 for (i = 0; i < info->num_cat; i++) {
527 if (info->cat[i].name) {
528 int j, name_len = strlen(info->cat[i].name)+1;
531 for (j = 0; j < name_len; j++)
532 name[j] = tolower(info->cat[i].name[j]);
534 name[name_len-1] = '\0';
535 ret = snprintf(str + offset, rem, "%s|", name+1);
538 OSMO_SNPRINTF_RET(ret, rem, offset, len);
541 offset--; /* to remove the trailing | */
544 ret = snprintf(str + offset, rem, ") (");
547 OSMO_SNPRINTF_RET(ret, rem, offset, len);
549 for (i = 0; i < LOGLEVEL_DEFS; i++) {
550 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
551 char loglevel_str[loglevel_str_len];
553 for (j = 0; j < loglevel_str_len; j++)
554 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
556 loglevel_str[loglevel_str_len-1] = '\0';
557 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
560 OSMO_SNPRINTF_RET(ret, rem, offset, len);
562 offset--; /* to remove the trailing | */
565 ret = snprintf(str + offset, rem, ")");
568 OSMO_SNPRINTF_RET(ret, rem, offset, len);
574 /* This generates the logging command description for VTY. */
575 const char *log_vty_command_description(const struct log_info *unused_info)
577 struct log_info *info = osmo_log_info;
579 int i, ret, len = 0, offset = 0, rem;
582 "Set the log level for a specified category\n") + 1;
584 for (i = 0; i < info->num_cat; i++) {
585 if (info->cat[i].name == NULL)
587 size += strlen(info->cat[i].description) + 1;
590 for (i = 0; i < LOGLEVEL_DEFS; i++)
591 size += strlen(loglevel_descriptions[i]) + 1;
593 size += strlen("Global setting for all subsystems") + 1;
595 str = talloc_zero_size(tall_log_ctx, size);
599 ret = snprintf(str + offset, rem, LOGGING_STR
600 "Set the log level for a specified category\n");
603 OSMO_SNPRINTF_RET(ret, rem, offset, len);
605 ret = snprintf(str + offset, rem,
606 "Global setting for all subsystems\n");
609 OSMO_SNPRINTF_RET(ret, rem, offset, len);
611 for (i = 0; i < info->num_cat; i++) {
612 if (info->cat[i].name == NULL)
614 ret = snprintf(str + offset, rem, "%s\n",
615 info->cat[i].description);
618 OSMO_SNPRINTF_RET(ret, rem, offset, len);
620 for (i = 0; i < LOGLEVEL_DEFS; i++) {
621 ret = snprintf(str + offset, rem, "%s\n",
622 loglevel_descriptions[i]);
625 OSMO_SNPRINTF_RET(ret, rem, offset, len);
632 int log_init(const struct log_info *inf, void *ctx)
636 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
640 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
644 osmo_log_info->num_cat_user = inf->num_cat;
645 /* total number = number of user cat + library cat */
646 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
648 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
650 osmo_log_info->num_cat);
651 if (!osmo_log_info->cat) {
652 talloc_free(osmo_log_info);
653 osmo_log_info = NULL;
657 /* copy over the user part */
658 for (i = 0; i < inf->num_cat; i++) {
659 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
660 sizeof(struct log_info_cat));
663 /* copy over the library part */
664 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
665 unsigned int cn = osmo_log_info->num_cat_user + i;
666 memcpy(&osmo_log_info->cat[cn],
667 &internal_cat[i], sizeof(struct log_info_cat));