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 /* You have to keep this in sync with the structure loglevel_strs. */
78 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
79 "Log simply everything",
80 "Log debug messages and higher levels",
81 "Log informational messages and higher levels",
82 "Log noticable messages and higher levels",
83 "Log error messages and higher levels",
84 "Log only fatal messages",
88 /* special magic for negative (library-internal) log subsystem numbers */
89 static int subsys_lib2index(int subsys)
91 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
94 int log_parse_level(const char *lvl)
96 return get_string_value(loglevel_strs, lvl);
99 const char *log_level_str(unsigned int lvl)
101 return get_value_string(loglevel_strs, lvl);
104 int log_parse_category(const char *category)
108 for (i = 0; i < osmo_log_info->num_cat; ++i) {
109 if (osmo_log_info->cat[i].name == NULL)
111 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
119 * Parse the category mask.
120 * The format can be this: category1:category2:category3
121 * or category1,2:category2,3:...
123 void log_parse_category_mask(struct log_target* target, const char *_mask)
126 char *mask = strdup(_mask);
127 char *category_token = NULL;
129 /* Disable everything to enable it afterwards */
130 for (i = 0; i < osmo_log_info->num_cat; ++i)
131 target->categories[i].enabled = 0;
133 category_token = strtok(mask, ":");
135 for (i = 0; i < osmo_log_info->num_cat; ++i) {
136 char* colon = strstr(category_token, ",");
137 int length = strlen(category_token);
139 if (!osmo_log_info->cat[i].name)
143 length = colon - category_token;
145 if (strncasecmp(osmo_log_info->cat[i].name,
146 category_token, length) == 0) {
150 level = atoi(colon+1);
152 target->categories[i].enabled = 1;
153 target->categories[i].loglevel = level;
156 } while ((category_token = strtok(NULL, ":")));
161 static const char* color(int subsys)
163 if (subsys < osmo_log_info->num_cat)
164 return osmo_log_info->cat[subsys].color;
169 static void _output(struct log_target *target, unsigned int subsys,
170 unsigned int level, char *file, int line, int cont,
171 const char *format, va_list ap)
174 int ret, len = 0, offset = 0, rem = sizeof(buf);
176 /* are we using color */
177 if (target->use_color) {
178 const char *c = color(subsys);
180 ret = snprintf(buf + offset, rem, "%s", color(subsys));
183 OSMO_SNPRINTF_RET(ret, rem, offset, len);
187 if (target->print_timestamp) {
191 timestr = ctime(&tm);
192 timestr[strlen(timestr)-1] = '\0';
193 ret = snprintf(buf + offset, rem, "%s ", timestr);
196 OSMO_SNPRINTF_RET(ret, rem, offset, len);
198 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
202 OSMO_SNPRINTF_RET(ret, rem, offset, len);
204 ret = vsnprintf(buf + offset, rem, format, ap);
207 OSMO_SNPRINTF_RET(ret, rem, offset, len);
209 ret = snprintf(buf + offset, rem, "%s",
210 target->use_color ? "\033[0;m" : "");
213 OSMO_SNPRINTF_RET(ret, rem, offset, len);
215 buf[sizeof(buf)-1] = '\0';
216 target->output(target, level, buf);
219 static void _logp(int subsys, int level, char *file, int line,
220 int cont, const char *format, va_list ap)
222 struct log_target *tar;
225 subsys = subsys_lib2index(subsys);
227 if (subsys > osmo_log_info->num_cat)
230 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
231 struct log_category *category;
235 category = &tar->categories[subsys];
236 /* subsystem is not supposed to be logged */
237 if (!category->enabled)
240 /* Check the global log level */
241 if (tar->loglevel != 0 && level < tar->loglevel)
244 /* Check the category log level */
245 if (tar->loglevel == 0 && category->loglevel != 0 &&
246 level < category->loglevel)
249 /* Apply filters here... if that becomes messy we will
250 * need to put filters in a list and each filter will
251 * say stop, continue, output */
252 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
254 else if (osmo_log_info->filter_fn)
255 output = osmo_log_info->filter_fn(&log_context,
260 /* According to the manpage, vsnprintf leaves the value of ap
261 * in undefined state. Since _output uses vsnprintf and it may
262 * be called several times, we have to pass a copy of ap. */
264 _output(tar, subsys, level, file, line, cont, format, bp);
269 void logp(int subsys, char *file, int line, int cont,
270 const char *format, ...)
274 va_start(ap, format);
275 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
279 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
283 va_start(ap, format);
284 _logp(subsys, level, file, line, cont, format, ap);
288 void log_add_target(struct log_target *target)
290 llist_add_tail(&target->entry, &osmo_log_target_list);
293 void log_del_target(struct log_target *target)
295 llist_del(&target->entry);
298 void log_reset_context(void)
300 memset(&log_context, 0, sizeof(log_context));
303 int log_set_context(uint8_t ctx_nr, void *value)
305 if (ctx_nr > LOG_MAX_CTX)
308 log_context.ctx[ctx_nr] = value;
313 void log_set_all_filter(struct log_target *target, int all)
316 target->filter_map |= LOG_FILTER_ALL;
318 target->filter_map &= ~LOG_FILTER_ALL;
321 void log_set_use_color(struct log_target *target, int use_color)
323 target->use_color = use_color;
326 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
328 target->print_timestamp = print_timestamp;
331 void log_set_log_level(struct log_target *target, int log_level)
333 target->loglevel = log_level;
336 void log_set_category_filter(struct log_target *target, int category,
337 int enable, int level)
339 if (category >= osmo_log_info->num_cat)
341 target->categories[category].enabled = !!enable;
342 target->categories[category].loglevel = level;
345 static void _file_output(struct log_target *target, unsigned int level,
348 fprintf(target->tgt_file.out, "%s", log);
349 fflush(target->tgt_file.out);
352 struct log_target *log_target_create(void)
354 struct log_target *target;
357 target = talloc_zero(tall_log_ctx, struct log_target);
361 target->categories = talloc_zero_array(target,
363 osmo_log_info->num_cat);
364 if (!target->categories) {
369 INIT_LLIST_HEAD(&target->entry);
371 /* initialize the per-category enabled/loglevel from defaults */
372 for (i = 0; i < osmo_log_info->num_cat; i++) {
373 struct log_category *cat = &target->categories[i];
374 cat->enabled = osmo_log_info->cat[i].enabled;
375 cat->loglevel = osmo_log_info->cat[i].loglevel;
378 /* global settings */
379 target->use_color = 1;
380 target->print_timestamp = 0;
382 /* global log level */
383 target->loglevel = 0;
387 struct log_target *log_target_create_stderr(void)
389 /* since C89/C99 says stderr is a macro, we can safely do this! */
391 struct log_target *target;
393 target = log_target_create();
397 target->type = LOG_TGT_TYPE_STDERR;
398 target->tgt_file.out = stderr;
399 target->output = _file_output;
406 struct log_target *log_target_create_file(const char *fname)
408 struct log_target *target;
410 target = log_target_create();
414 target->type = LOG_TGT_TYPE_FILE;
415 target->tgt_file.out = fopen(fname, "a");
416 if (!target->tgt_file.out)
419 target->output = _file_output;
421 target->tgt_file.fname = talloc_strdup(target, fname);
426 struct log_target *log_target_find(int type, const char *fname)
428 struct log_target *tgt;
430 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
431 if (tgt->type != type)
433 if (tgt->type == LOG_TGT_TYPE_FILE) {
434 if (!strcmp(fname, tgt->tgt_file.fname))
442 void log_target_destroy(struct log_target *target)
445 /* just in case, to make sure we don't have any references */
446 log_del_target(target);
448 if (target->output == &_file_output) {
449 /* since C89/C99 says stderr is a macro, we can safely do this! */
451 /* don't close stderr */
452 if (target->tgt_file.out != stderr)
455 fclose(target->tgt_file.out);
456 target->tgt_file.out = NULL;
463 /* close and re-open a log file (for log file rotation) */
464 int log_target_file_reopen(struct log_target *target)
466 fclose(target->tgt_file.out);
468 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
469 if (!target->tgt_file.out)
472 /* we assume target->output already to be set */
477 /* This generates the logging command string for VTY. */
478 const char *log_vty_command_string(const struct log_info *unused_info)
480 struct log_info *info = osmo_log_info;
481 int len = 0, offset = 0, ret, i, rem;
482 int size = strlen("logging level () ()") + 1;
485 for (i = 0; i < info->num_cat; i++) {
486 if (info->cat[i].name == NULL)
488 size += strlen(info->cat[i].name) + 1;
491 for (i = 0; i < LOGLEVEL_DEFS; i++)
492 size += strlen(loglevel_strs[i].str) + 1;
495 str = talloc_zero_size(tall_log_ctx, size);
499 ret = snprintf(str + offset, rem, "logging level (all|");
502 OSMO_SNPRINTF_RET(ret, rem, offset, len);
504 for (i = 0; i < info->num_cat; i++) {
505 if (info->cat[i].name) {
506 int j, name_len = strlen(info->cat[i].name)+1;
509 for (j = 0; j < name_len; j++)
510 name[j] = tolower(info->cat[i].name[j]);
512 name[name_len-1] = '\0';
513 ret = snprintf(str + offset, rem, "%s|", name+1);
516 OSMO_SNPRINTF_RET(ret, rem, offset, len);
519 offset--; /* to remove the trailing | */
522 ret = snprintf(str + offset, rem, ") (");
525 OSMO_SNPRINTF_RET(ret, rem, offset, len);
527 for (i = 0; i < LOGLEVEL_DEFS; i++) {
528 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
529 char loglevel_str[loglevel_str_len];
531 for (j = 0; j < loglevel_str_len; j++)
532 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
534 loglevel_str[loglevel_str_len-1] = '\0';
535 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
538 OSMO_SNPRINTF_RET(ret, rem, offset, len);
540 offset--; /* to remove the trailing | */
543 ret = snprintf(str + offset, rem, ")");
546 OSMO_SNPRINTF_RET(ret, rem, offset, len);
552 /* This generates the logging command description for VTY. */
553 const char *log_vty_command_description(const struct log_info *unused_info)
555 struct log_info *info = osmo_log_info;
557 int i, ret, len = 0, offset = 0, rem;
560 "Set the log level for a specified category\n") + 1;
562 for (i = 0; i < info->num_cat; i++) {
563 if (info->cat[i].name == NULL)
565 size += strlen(info->cat[i].description) + 1;
568 for (i = 0; i < LOGLEVEL_DEFS; i++)
569 size += strlen(loglevel_descriptions[i]) + 1;
571 size += strlen("Global setting for all subsystems") + 1;
573 str = talloc_zero_size(tall_log_ctx, size);
577 ret = snprintf(str + offset, rem, LOGGING_STR
578 "Set the log level for a specified category\n");
581 OSMO_SNPRINTF_RET(ret, rem, offset, len);
583 ret = snprintf(str + offset, rem,
584 "Global setting for all subsystems\n");
587 OSMO_SNPRINTF_RET(ret, rem, offset, len);
589 for (i = 0; i < info->num_cat; i++) {
590 if (info->cat[i].name == NULL)
592 ret = snprintf(str + offset, rem, "%s\n",
593 info->cat[i].description);
596 OSMO_SNPRINTF_RET(ret, rem, offset, len);
598 for (i = 0; i < LOGLEVEL_DEFS; i++) {
599 ret = snprintf(str + offset, rem, "%s\n",
600 loglevel_descriptions[i]);
603 OSMO_SNPRINTF_RET(ret, rem, offset, len);
610 int log_init(const struct log_info *inf, void *ctx)
614 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
618 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
622 osmo_log_info->num_cat_user = inf->num_cat;
623 /* total number = number of user cat + library cat */
624 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
626 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
628 osmo_log_info->num_cat);
629 if (!osmo_log_info->cat) {
630 talloc_free(osmo_log_info);
631 osmo_log_info = NULL;
635 /* copy over the user part */
636 for (i = 0; i < inf->num_cat; i++) {
637 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
638 sizeof(struct log_info_cat));
641 /* copy over the library part */
642 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
643 unsigned int cn = osmo_log_info->num_cat_user + i;
644 memcpy(&osmo_log_info->cat[cn],
645 &internal_cat[i], sizeof(struct log_info_cat));