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,
71 /* You have to keep this in sync with the structure loglevel_strs. */
72 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
73 "Log simply everything",
74 "Log debug messages and higher levels",
75 "Log informational messages and higher levels",
76 "Log noticable messages and higher levels",
77 "Log error messages and higher levels",
78 "Log only fatal messages",
82 /* special magic for negative (library-internal) log subsystem numbers */
83 static int subsys_lib2index(int subsys)
85 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
88 int log_parse_level(const char *lvl)
90 return get_string_value(loglevel_strs, lvl);
93 const char *log_level_str(unsigned int lvl)
95 return get_value_string(loglevel_strs, lvl);
98 int log_parse_category(const char *category)
102 for (i = 0; i < osmo_log_info->num_cat; ++i) {
103 if (osmo_log_info->cat[i].name == NULL)
105 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
113 * Parse the category mask.
114 * The format can be this: category1:category2:category3
115 * or category1,2:category2,3:...
117 void log_parse_category_mask(struct log_target* target, const char *_mask)
120 char *mask = strdup(_mask);
121 char *category_token = NULL;
123 /* Disable everything to enable it afterwards */
124 for (i = 0; i < osmo_log_info->num_cat; ++i)
125 target->categories[i].enabled = 0;
127 category_token = strtok(mask, ":");
129 for (i = 0; i < osmo_log_info->num_cat; ++i) {
130 char* colon = strstr(category_token, ",");
131 int length = strlen(category_token);
133 if (!osmo_log_info->cat[i].name)
137 length = colon - category_token;
139 if (strncasecmp(osmo_log_info->cat[i].name,
140 category_token, length) == 0) {
144 level = atoi(colon+1);
146 target->categories[i].enabled = 1;
147 target->categories[i].loglevel = level;
150 } while ((category_token = strtok(NULL, ":")));
155 static const char* color(int subsys)
157 if (subsys < osmo_log_info->num_cat)
158 return osmo_log_info->cat[subsys].color;
163 static void _output(struct log_target *target, unsigned int subsys,
164 unsigned int level, char *file, int line, int cont,
165 const char *format, va_list ap)
168 int ret, len = 0, offset = 0, rem = sizeof(buf);
170 /* are we using color */
171 if (target->use_color) {
172 const char *c = color(subsys);
174 ret = snprintf(buf + offset, rem, "%s", color(subsys));
177 OSMO_SNPRINTF_RET(ret, rem, offset, len);
181 if (target->print_timestamp) {
185 timestr = ctime(&tm);
186 timestr[strlen(timestr)-1] = '\0';
187 ret = snprintf(buf + offset, rem, "%s ", timestr);
190 OSMO_SNPRINTF_RET(ret, rem, offset, len);
192 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
196 OSMO_SNPRINTF_RET(ret, rem, offset, len);
198 ret = vsnprintf(buf + offset, rem, format, ap);
201 OSMO_SNPRINTF_RET(ret, rem, offset, len);
203 ret = snprintf(buf + offset, rem, "%s",
204 target->use_color ? "\033[0;m" : "");
207 OSMO_SNPRINTF_RET(ret, rem, offset, len);
209 buf[sizeof(buf)-1] = '\0';
210 target->output(target, level, buf);
213 static void _logp(int subsys, int level, char *file, int line,
214 int cont, const char *format, va_list ap)
216 struct log_target *tar;
219 subsys = subsys_lib2index(subsys);
221 if (subsys > osmo_log_info->num_cat)
224 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
225 struct log_category *category;
229 category = &tar->categories[subsys];
230 /* subsystem is not supposed to be logged */
231 if (!category->enabled)
234 /* Check the global log level */
235 if (tar->loglevel != 0 && level < tar->loglevel)
238 /* Check the category log level */
239 if (tar->loglevel == 0 && category->loglevel != 0 &&
240 level < category->loglevel)
243 /* Apply filters here... if that becomes messy we will
244 * need to put filters in a list and each filter will
245 * say stop, continue, output */
246 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
248 else if (osmo_log_info->filter_fn)
249 output = osmo_log_info->filter_fn(&log_context,
254 /* According to the manpage, vsnprintf leaves the value of ap
255 * in undefined state. Since _output uses vsnprintf and it may
256 * be called several times, we have to pass a copy of ap. */
258 _output(tar, subsys, level, file, line, cont, format, ap);
263 void logp(int subsys, char *file, int line, int cont,
264 const char *format, ...)
268 va_start(ap, format);
269 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
273 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
277 va_start(ap, format);
278 _logp(subsys, level, file, line, cont, format, ap);
282 void log_add_target(struct log_target *target)
284 llist_add_tail(&target->entry, &osmo_log_target_list);
287 void log_del_target(struct log_target *target)
289 llist_del(&target->entry);
292 void log_reset_context(void)
294 memset(&log_context, 0, sizeof(log_context));
297 int log_set_context(uint8_t ctx_nr, void *value)
299 if (ctx_nr > LOG_MAX_CTX)
302 log_context.ctx[ctx_nr] = value;
307 void log_set_all_filter(struct log_target *target, int all)
310 target->filter_map |= LOG_FILTER_ALL;
312 target->filter_map &= ~LOG_FILTER_ALL;
315 void log_set_use_color(struct log_target *target, int use_color)
317 target->use_color = use_color;
320 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
322 target->print_timestamp = print_timestamp;
325 void log_set_log_level(struct log_target *target, int log_level)
327 target->loglevel = log_level;
330 void log_set_category_filter(struct log_target *target, int category,
331 int enable, int level)
333 if (category >= osmo_log_info->num_cat)
335 target->categories[category].enabled = !!enable;
336 target->categories[category].loglevel = level;
339 static void _file_output(struct log_target *target, unsigned int level,
342 fprintf(target->tgt_file.out, "%s", log);
343 fflush(target->tgt_file.out);
346 struct log_target *log_target_create(void)
348 struct log_target *target;
351 target = talloc_zero(tall_log_ctx, struct log_target);
355 target->categories = talloc_zero_array(target,
357 osmo_log_info->num_cat);
358 if (!target->categories) {
363 INIT_LLIST_HEAD(&target->entry);
365 /* initialize the per-category enabled/loglevel from defaults */
366 for (i = 0; i < osmo_log_info->num_cat; i++) {
367 struct log_category *cat = &target->categories[i];
368 cat->enabled = osmo_log_info->cat[i].enabled;
369 cat->loglevel = osmo_log_info->cat[i].loglevel;
372 /* global settings */
373 target->use_color = 1;
374 target->print_timestamp = 0;
376 /* global log level */
377 target->loglevel = 0;
381 struct log_target *log_target_create_stderr(void)
383 /* since C89/C99 says stderr is a macro, we can safely do this! */
385 struct log_target *target;
387 target = log_target_create();
391 target->type = LOG_TGT_TYPE_STDERR;
392 target->tgt_file.out = stderr;
393 target->output = _file_output;
400 struct log_target *log_target_create_file(const char *fname)
402 struct log_target *target;
404 target = log_target_create();
408 target->type = LOG_TGT_TYPE_FILE;
409 target->tgt_file.out = fopen(fname, "a");
410 if (!target->tgt_file.out)
413 target->output = _file_output;
415 target->tgt_file.fname = talloc_strdup(target, fname);
420 struct log_target *log_target_find(int type, const char *fname)
422 struct log_target *tgt;
424 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
425 if (tgt->type != type)
427 if (tgt->type == LOG_TGT_TYPE_FILE) {
428 if (!strcmp(fname, tgt->tgt_file.fname))
436 void log_target_destroy(struct log_target *target)
439 /* just in case, to make sure we don't have any references */
440 log_del_target(target);
442 if (target->output == &_file_output) {
443 /* since C89/C99 says stderr is a macro, we can safely do this! */
445 /* don't close stderr */
446 if (target->tgt_file.out != stderr)
449 fclose(target->tgt_file.out);
450 target->tgt_file.out = NULL;
457 /* close and re-open a log file (for log file rotation) */
458 int log_target_file_reopen(struct log_target *target)
460 fclose(target->tgt_file.out);
462 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
463 if (!target->tgt_file.out)
466 /* we assume target->output already to be set */
471 /* This generates the logging command string for VTY. */
472 const char *log_vty_command_string(const struct log_info *info)
474 int len = 0, offset = 0, ret, i, rem;
475 int size = strlen("logging level () ()") + 1;
478 for (i = 0; i < info->num_cat; i++) {
479 if (info->cat[i].name == NULL)
481 size += strlen(info->cat[i].name) + 1;
484 for (i = 0; i < LOGLEVEL_DEFS; i++)
485 size += strlen(loglevel_strs[i].str) + 1;
488 str = talloc_zero_size(tall_log_ctx, size);
492 ret = snprintf(str + offset, rem, "logging level (all|");
495 OSMO_SNPRINTF_RET(ret, rem, offset, len);
497 for (i = 0; i < info->num_cat; i++) {
498 if (info->cat[i].name) {
499 int j, name_len = strlen(info->cat[i].name)+1;
502 for (j = 0; j < name_len; j++)
503 name[j] = tolower(info->cat[i].name[j]);
505 name[name_len-1] = '\0';
506 ret = snprintf(str + offset, rem, "%s|", name+1);
509 OSMO_SNPRINTF_RET(ret, rem, offset, len);
512 offset--; /* to remove the trailing | */
515 ret = snprintf(str + offset, rem, ") (");
518 OSMO_SNPRINTF_RET(ret, rem, offset, len);
520 for (i = 0; i < LOGLEVEL_DEFS; i++) {
521 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
522 char loglevel_str[loglevel_str_len];
524 for (j = 0; j < loglevel_str_len; j++)
525 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
527 loglevel_str[loglevel_str_len-1] = '\0';
528 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
531 OSMO_SNPRINTF_RET(ret, rem, offset, len);
533 offset--; /* to remove the trailing | */
536 ret = snprintf(str + offset, rem, ")");
539 OSMO_SNPRINTF_RET(ret, rem, offset, len);
545 /* This generates the logging command description for VTY. */
546 const char *log_vty_command_description(const struct log_info *info)
549 int i, ret, len = 0, offset = 0, rem;
552 "Set the log level for a specified category\n") + 1;
554 for (i = 0; i < info->num_cat; i++) {
555 if (info->cat[i].name == NULL)
557 size += strlen(info->cat[i].description) + 1;
560 for (i = 0; i < LOGLEVEL_DEFS; i++)
561 size += strlen(loglevel_descriptions[i]) + 1;
563 size += strlen("Global setting for all subsystems") + 1;
565 str = talloc_zero_size(tall_log_ctx, size);
569 ret = snprintf(str + offset, rem, LOGGING_STR
570 "Set the log level for a specified category\n");
573 OSMO_SNPRINTF_RET(ret, rem, offset, len);
575 ret = snprintf(str + offset, rem,
576 "Global setting for all subsystems\n");
579 OSMO_SNPRINTF_RET(ret, rem, offset, len);
581 for (i = 0; i < info->num_cat; i++) {
582 if (info->cat[i].name == NULL)
584 ret = snprintf(str + offset, rem, "%s\n",
585 info->cat[i].description);
588 OSMO_SNPRINTF_RET(ret, rem, offset, len);
590 for (i = 0; i < LOGLEVEL_DEFS; i++) {
591 ret = snprintf(str + offset, rem, "%s\n",
592 loglevel_descriptions[i]);
595 OSMO_SNPRINTF_RET(ret, rem, offset, len);
602 int log_init(const struct log_info *inf, void *ctx)
606 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
610 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
614 osmo_log_info->num_cat_user = inf->num_cat;
615 /* total number = number of user cat + library cat */
616 osmo_log_info->num_cat = inf->num_cat + OSMO_NUM_DLIB;
618 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
620 osmo_log_info->num_cat);
621 if (!osmo_log_info->cat) {
622 talloc_free(osmo_log_info);
623 osmo_log_info = NULL;
627 /* copy over the user part */
628 for (i = 0; i < inf->num_cat; i++) {
629 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
630 sizeof(struct log_info_cat));
633 /* copy over the library part */