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 .description = "A-bis Radio Siganlling Link (RSL)",
100 .color = "\033[1;35m",
101 .enabled = 1, .loglevel = LOGL_NOTICE,
105 .description = "A-bis Network Management / O&M (NM/OML)",
106 .color = "\033[1;36m",
107 .enabled = 1, .loglevel = LOGL_INFO,
111 /* You have to keep this in sync with the structure loglevel_strs. */
112 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
113 "Log simply everything",
114 "Log debug messages and higher levels",
115 "Log informational messages and higher levels",
116 "Log noticable messages and higher levels",
117 "Log error messages and higher levels",
118 "Log only fatal messages",
122 /* special magic for negative (library-internal) log subsystem numbers */
123 static int subsys_lib2index(int subsys)
125 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
128 int log_parse_level(const char *lvl)
130 return get_string_value(loglevel_strs, lvl);
133 const char *log_level_str(unsigned int lvl)
135 return get_value_string(loglevel_strs, lvl);
138 int log_parse_category(const char *category)
142 for (i = 0; i < osmo_log_info->num_cat; ++i) {
143 if (osmo_log_info->cat[i].name == NULL)
145 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
153 * Parse the category mask.
154 * The format can be this: category1:category2:category3
155 * or category1,2:category2,3:...
157 void log_parse_category_mask(struct log_target* target, const char *_mask)
160 char *mask = strdup(_mask);
161 char *category_token = NULL;
163 /* Disable everything to enable it afterwards */
164 for (i = 0; i < osmo_log_info->num_cat; ++i)
165 target->categories[i].enabled = 0;
167 category_token = strtok(mask, ":");
169 for (i = 0; i < osmo_log_info->num_cat; ++i) {
170 char* colon = strstr(category_token, ",");
171 int length = strlen(category_token);
173 if (!osmo_log_info->cat[i].name)
177 length = colon - category_token;
179 if (strncasecmp(osmo_log_info->cat[i].name,
180 category_token, length) == 0) {
184 level = atoi(colon+1);
186 target->categories[i].enabled = 1;
187 target->categories[i].loglevel = level;
190 } while ((category_token = strtok(NULL, ":")));
195 static const char* color(int subsys)
197 if (subsys < osmo_log_info->num_cat)
198 return osmo_log_info->cat[subsys].color;
203 static void _output(struct log_target *target, unsigned int subsys,
204 unsigned int level, char *file, int line, int cont,
205 const char *format, va_list ap)
208 int ret, len = 0, offset = 0, rem = sizeof(buf);
210 /* are we using color */
211 if (target->use_color) {
212 const char *c = color(subsys);
214 ret = snprintf(buf + offset, rem, "%s", color(subsys));
217 OSMO_SNPRINTF_RET(ret, rem, offset, len);
221 if (target->print_timestamp) {
225 timestr = ctime(&tm);
226 timestr[strlen(timestr)-1] = '\0';
227 ret = snprintf(buf + offset, rem, "%s ", timestr);
230 OSMO_SNPRINTF_RET(ret, rem, offset, len);
232 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
236 OSMO_SNPRINTF_RET(ret, rem, offset, len);
238 ret = vsnprintf(buf + offset, rem, format, ap);
241 OSMO_SNPRINTF_RET(ret, rem, offset, len);
243 ret = snprintf(buf + offset, rem, "%s",
244 target->use_color ? "\033[0;m" : "");
247 OSMO_SNPRINTF_RET(ret, rem, offset, len);
249 buf[sizeof(buf)-1] = '\0';
250 target->output(target, level, buf);
253 static void _logp(int subsys, int level, char *file, int line,
254 int cont, const char *format, va_list ap)
256 struct log_target *tar;
259 subsys = subsys_lib2index(subsys);
261 if (subsys > osmo_log_info->num_cat)
264 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
265 struct log_category *category;
269 category = &tar->categories[subsys];
270 /* subsystem is not supposed to be logged */
271 if (!category->enabled)
274 /* Check the global log level */
275 if (tar->loglevel != 0 && level < tar->loglevel)
278 /* Check the category log level */
279 if (tar->loglevel == 0 && category->loglevel != 0 &&
280 level < category->loglevel)
283 /* Apply filters here... if that becomes messy we will
284 * need to put filters in a list and each filter will
285 * say stop, continue, output */
286 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
288 else if (osmo_log_info->filter_fn)
289 output = osmo_log_info->filter_fn(&log_context,
294 /* According to the manpage, vsnprintf leaves the value of ap
295 * in undefined state. Since _output uses vsnprintf and it may
296 * be called several times, we have to pass a copy of ap. */
298 _output(tar, subsys, level, file, line, cont, format, bp);
303 void logp(int subsys, char *file, int line, int cont,
304 const char *format, ...)
308 va_start(ap, format);
309 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
313 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
317 va_start(ap, format);
318 _logp(subsys, level, file, line, cont, format, ap);
322 void log_add_target(struct log_target *target)
324 llist_add_tail(&target->entry, &osmo_log_target_list);
327 void log_del_target(struct log_target *target)
329 llist_del(&target->entry);
332 void log_reset_context(void)
334 memset(&log_context, 0, sizeof(log_context));
337 int log_set_context(uint8_t ctx_nr, void *value)
339 if (ctx_nr > LOG_MAX_CTX)
342 log_context.ctx[ctx_nr] = value;
347 void log_set_all_filter(struct log_target *target, int all)
350 target->filter_map |= LOG_FILTER_ALL;
352 target->filter_map &= ~LOG_FILTER_ALL;
355 void log_set_use_color(struct log_target *target, int use_color)
357 target->use_color = use_color;
360 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
362 target->print_timestamp = print_timestamp;
365 void log_set_log_level(struct log_target *target, int log_level)
367 target->loglevel = log_level;
370 void log_set_category_filter(struct log_target *target, int category,
371 int enable, int level)
373 if (category >= osmo_log_info->num_cat)
375 target->categories[category].enabled = !!enable;
376 target->categories[category].loglevel = level;
379 static void _file_output(struct log_target *target, unsigned int level,
382 fprintf(target->tgt_file.out, "%s", log);
383 fflush(target->tgt_file.out);
386 struct log_target *log_target_create(void)
388 struct log_target *target;
391 target = talloc_zero(tall_log_ctx, struct log_target);
395 target->categories = talloc_zero_array(target,
397 osmo_log_info->num_cat);
398 if (!target->categories) {
403 INIT_LLIST_HEAD(&target->entry);
405 /* initialize the per-category enabled/loglevel from defaults */
406 for (i = 0; i < osmo_log_info->num_cat; i++) {
407 struct log_category *cat = &target->categories[i];
408 cat->enabled = osmo_log_info->cat[i].enabled;
409 cat->loglevel = osmo_log_info->cat[i].loglevel;
412 /* global settings */
413 target->use_color = 1;
414 target->print_timestamp = 0;
416 /* global log level */
417 target->loglevel = 0;
421 struct log_target *log_target_create_stderr(void)
423 /* since C89/C99 says stderr is a macro, we can safely do this! */
425 struct log_target *target;
427 target = log_target_create();
431 target->type = LOG_TGT_TYPE_STDERR;
432 target->tgt_file.out = stderr;
433 target->output = _file_output;
440 struct log_target *log_target_create_file(const char *fname)
442 struct log_target *target;
444 target = log_target_create();
448 target->type = LOG_TGT_TYPE_FILE;
449 target->tgt_file.out = fopen(fname, "a");
450 if (!target->tgt_file.out)
453 target->output = _file_output;
455 target->tgt_file.fname = talloc_strdup(target, fname);
460 struct log_target *log_target_find(int type, const char *fname)
462 struct log_target *tgt;
464 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
465 if (tgt->type != type)
467 if (tgt->type == LOG_TGT_TYPE_FILE) {
468 if (!strcmp(fname, tgt->tgt_file.fname))
476 void log_target_destroy(struct log_target *target)
479 /* just in case, to make sure we don't have any references */
480 log_del_target(target);
482 if (target->output == &_file_output) {
483 /* since C89/C99 says stderr is a macro, we can safely do this! */
485 /* don't close stderr */
486 if (target->tgt_file.out != stderr)
489 fclose(target->tgt_file.out);
490 target->tgt_file.out = NULL;
497 /* close and re-open a log file (for log file rotation) */
498 int log_target_file_reopen(struct log_target *target)
500 fclose(target->tgt_file.out);
502 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
503 if (!target->tgt_file.out)
506 /* we assume target->output already to be set */
511 /* This generates the logging command string for VTY. */
512 const char *log_vty_command_string(const struct log_info *unused_info)
514 struct log_info *info = osmo_log_info;
515 int len = 0, offset = 0, ret, i, rem;
516 int size = strlen("logging level () ()") + 1;
519 for (i = 0; i < info->num_cat; i++) {
520 if (info->cat[i].name == NULL)
522 size += strlen(info->cat[i].name) + 1;
525 for (i = 0; i < LOGLEVEL_DEFS; i++)
526 size += strlen(loglevel_strs[i].str) + 1;
529 str = talloc_zero_size(tall_log_ctx, size);
533 ret = snprintf(str + offset, rem, "logging level (all|");
536 OSMO_SNPRINTF_RET(ret, rem, offset, len);
538 for (i = 0; i < info->num_cat; i++) {
539 if (info->cat[i].name) {
540 int j, name_len = strlen(info->cat[i].name)+1;
543 for (j = 0; j < name_len; j++)
544 name[j] = tolower(info->cat[i].name[j]);
546 name[name_len-1] = '\0';
547 ret = snprintf(str + offset, rem, "%s|", name+1);
550 OSMO_SNPRINTF_RET(ret, rem, offset, len);
553 offset--; /* to remove the trailing | */
556 ret = snprintf(str + offset, rem, ") (");
559 OSMO_SNPRINTF_RET(ret, rem, offset, len);
561 for (i = 0; i < LOGLEVEL_DEFS; i++) {
562 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
563 char loglevel_str[loglevel_str_len];
565 for (j = 0; j < loglevel_str_len; j++)
566 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
568 loglevel_str[loglevel_str_len-1] = '\0';
569 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
572 OSMO_SNPRINTF_RET(ret, rem, offset, len);
574 offset--; /* to remove the trailing | */
577 ret = snprintf(str + offset, rem, ")");
580 OSMO_SNPRINTF_RET(ret, rem, offset, len);
586 /* This generates the logging command description for VTY. */
587 const char *log_vty_command_description(const struct log_info *unused_info)
589 struct log_info *info = osmo_log_info;
591 int i, ret, len = 0, offset = 0, rem;
594 "Set the log level for a specified category\n") + 1;
596 for (i = 0; i < info->num_cat; i++) {
597 if (info->cat[i].name == NULL)
599 size += strlen(info->cat[i].description) + 1;
602 for (i = 0; i < LOGLEVEL_DEFS; i++)
603 size += strlen(loglevel_descriptions[i]) + 1;
605 size += strlen("Global setting for all subsystems") + 1;
607 str = talloc_zero_size(tall_log_ctx, size);
611 ret = snprintf(str + offset, rem, LOGGING_STR
612 "Set the log level for a specified category\n");
615 OSMO_SNPRINTF_RET(ret, rem, offset, len);
617 ret = snprintf(str + offset, rem,
618 "Global setting for all subsystems\n");
621 OSMO_SNPRINTF_RET(ret, rem, offset, len);
623 for (i = 0; i < info->num_cat; i++) {
624 if (info->cat[i].name == NULL)
626 ret = snprintf(str + offset, rem, "%s\n",
627 info->cat[i].description);
630 OSMO_SNPRINTF_RET(ret, rem, offset, len);
632 for (i = 0; i < LOGLEVEL_DEFS; i++) {
633 ret = snprintf(str + offset, rem, "%s\n",
634 loglevel_descriptions[i]);
637 OSMO_SNPRINTF_RET(ret, rem, offset, len);
644 int log_init(const struct log_info *inf, void *ctx)
648 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
652 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
656 osmo_log_info->num_cat_user = inf->num_cat;
657 /* total number = number of user cat + library cat */
658 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
660 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
662 osmo_log_info->num_cat);
663 if (!osmo_log_info->cat) {
664 talloc_free(osmo_log_info);
665 osmo_log_info = NULL;
669 /* copy over the user part */
670 for (i = 0; i < inf->num_cat; i++) {
671 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
672 sizeof(struct log_info_cat));
675 /* copy over the library part */
676 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
677 unsigned int cn = osmo_log_info->num_cat_user + i;
678 memcpy(&osmo_log_info->cat[cn],
679 &internal_cat[i], sizeof(struct log_info_cat));