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 const 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 /* 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",
72 int log_parse_level(const char *lvl)
74 return get_string_value(loglevel_strs, lvl);
77 const char *log_level_str(unsigned int lvl)
79 return get_value_string(loglevel_strs, lvl);
82 int log_parse_category(const char *category)
86 for (i = 0; i < osmo_log_info->num_cat; ++i) {
87 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
95 * Parse the category mask.
96 * The format can be this: category1:category2:category3
97 * or category1,2:category2,3:...
99 void log_parse_category_mask(struct log_target* target, const char *_mask)
102 char *mask = strdup(_mask);
103 char *category_token = NULL;
105 /* Disable everything to enable it afterwards */
106 for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
107 target->categories[i].enabled = 0;
109 category_token = strtok(mask, ":");
111 for (i = 0; i < osmo_log_info->num_cat; ++i) {
112 char* colon = strstr(category_token, ",");
113 int length = strlen(category_token);
116 length = colon - category_token;
118 if (strncasecmp(osmo_log_info->cat[i].name,
119 category_token, length) == 0) {
123 level = atoi(colon+1);
125 target->categories[i].enabled = 1;
126 target->categories[i].loglevel = level;
129 } while ((category_token = strtok(NULL, ":")));
134 static const char* color(int subsys)
136 if (subsys < osmo_log_info->num_cat)
137 return osmo_log_info->cat[subsys].color;
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)
147 int ret, len = 0, offset = 0, rem = sizeof(buf);
149 /* are we using color */
150 if (target->use_color) {
151 const char *c = color(subsys);
153 ret = snprintf(buf + offset, rem, "%s", color(subsys));
156 OSMO_SNPRINTF_RET(ret, rem, offset, len);
160 if (target->print_timestamp) {
164 timestr = ctime(&tm);
165 timestr[strlen(timestr)-1] = '\0';
166 ret = snprintf(buf + offset, rem, "%s ", timestr);
169 OSMO_SNPRINTF_RET(ret, rem, offset, len);
171 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
175 OSMO_SNPRINTF_RET(ret, rem, offset, len);
177 ret = vsnprintf(buf + offset, rem, format, ap);
180 OSMO_SNPRINTF_RET(ret, rem, offset, len);
182 ret = snprintf(buf + offset, rem, "%s",
183 target->use_color ? "\033[0;m" : "");
186 OSMO_SNPRINTF_RET(ret, rem, offset, len);
188 buf[sizeof(buf)-1] = '\0';
189 target->output(target, level, buf);
193 static void _logp(unsigned int subsys, int level, char *file, int line,
194 int cont, const char *format, va_list ap)
196 struct log_target *tar;
198 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
199 struct log_category *category;
202 category = &tar->categories[subsys];
203 /* subsystem is not supposed to be logged */
204 if (!category->enabled)
207 /* Check the global log level */
208 if (tar->loglevel != 0 && level < tar->loglevel)
211 /* Check the category log level */
212 if (tar->loglevel == 0 && category->loglevel != 0 &&
213 level < category->loglevel)
216 /* Apply filters here... if that becomes messy we will
217 * need to put filters in a list and each filter will
218 * say stop, continue, output */
219 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
221 else if (osmo_log_info->filter_fn)
222 output = osmo_log_info->filter_fn(&log_context,
227 _output(tar, subsys, level, file, line, cont, format, ap);
231 void logp(unsigned int subsys, char *file, int line, int cont,
232 const char *format, ...)
236 va_start(ap, format);
237 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
241 void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
245 va_start(ap, format);
246 _logp(subsys, level, file, line, cont, format, ap);
250 void log_add_target(struct log_target *target)
252 llist_add_tail(&target->entry, &osmo_log_target_list);
255 void log_del_target(struct log_target *target)
257 llist_del(&target->entry);
260 void log_reset_context(void)
262 memset(&log_context, 0, sizeof(log_context));
265 int log_set_context(uint8_t ctx_nr, void *value)
267 if (ctx_nr > LOG_MAX_CTX)
270 log_context.ctx[ctx_nr] = value;
275 void log_set_all_filter(struct log_target *target, int all)
278 target->filter_map |= LOG_FILTER_ALL;
280 target->filter_map &= ~LOG_FILTER_ALL;
283 void log_set_use_color(struct log_target *target, int use_color)
285 target->use_color = use_color;
288 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
290 target->print_timestamp = print_timestamp;
293 void log_set_log_level(struct log_target *target, int log_level)
295 target->loglevel = log_level;
298 void log_set_category_filter(struct log_target *target, int category,
299 int enable, int level)
301 if (category >= osmo_log_info->num_cat)
303 target->categories[category].enabled = !!enable;
304 target->categories[category].loglevel = level;
307 static void _file_output(struct log_target *target, unsigned int level,
310 fprintf(target->tgt_file.out, "%s", log);
311 fflush(target->tgt_file.out);
314 struct log_target *log_target_create(void)
316 struct log_target *target;
319 target = talloc_zero(tall_log_ctx, struct log_target);
323 INIT_LLIST_HEAD(&target->entry);
325 /* initialize the per-category enabled/loglevel from defaults */
326 for (i = 0; i < osmo_log_info->num_cat; i++) {
327 struct log_category *cat = &target->categories[i];
328 cat->enabled = osmo_log_info->cat[i].enabled;
329 cat->loglevel = osmo_log_info->cat[i].loglevel;
332 /* global settings */
333 target->use_color = 1;
334 target->print_timestamp = 0;
336 /* global log level */
337 target->loglevel = 0;
341 struct log_target *log_target_create_stderr(void)
343 /* since C89/C99 says stderr is a macro, we can safely do this! */
345 struct log_target *target;
347 target = log_target_create();
351 target->type = LOG_TGT_TYPE_STDERR;
352 target->tgt_file.out = stderr;
353 target->output = _file_output;
360 struct log_target *log_target_create_file(const char *fname)
362 struct log_target *target;
364 target = log_target_create();
368 target->type = LOG_TGT_TYPE_FILE;
369 target->tgt_file.out = fopen(fname, "a");
370 if (!target->tgt_file.out)
373 target->output = _file_output;
375 target->tgt_file.fname = talloc_strdup(target, fname);
380 struct log_target *log_target_find(int type, const char *fname)
382 struct log_target *tgt;
384 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
385 if (tgt->type != type)
387 if (tgt->type == LOG_TGT_TYPE_FILE) {
388 if (!strcmp(fname, tgt->tgt_file.fname))
396 void log_target_destroy(struct log_target *target)
399 /* just in case, to make sure we don't have any references */
400 log_del_target(target);
402 if (target->output == &_file_output) {
403 /* since C89/C99 says stderr is a macro, we can safely do this! */
405 /* don't close stderr */
406 if (target->tgt_file.out != stderr)
409 fclose(target->tgt_file.out);
410 target->tgt_file.out = NULL;
417 /* close and re-open a log file (for log file rotation) */
418 int log_target_file_reopen(struct log_target *target)
420 fclose(target->tgt_file.out);
422 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
423 if (!target->tgt_file.out)
426 /* we assume target->output already to be set */
431 /* This generates the logging command string for VTY. */
432 const char *log_vty_command_string(const struct log_info *info)
434 int len = 0, offset = 0, ret, i, rem;
435 int size = strlen("logging level () ()") + 1;
438 for (i = 0; i < info->num_cat; i++)
439 size += strlen(info->cat[i].name) + 1;
441 for (i = 0; i < LOGLEVEL_DEFS; i++)
442 size += strlen(loglevel_strs[i].str) + 1;
445 str = talloc_zero_size(tall_log_ctx, size);
449 ret = snprintf(str + offset, rem, "logging level (all|");
452 OSMO_SNPRINTF_RET(ret, rem, offset, len);
454 for (i = 0; i < info->num_cat; i++) {
455 int j, name_len = strlen(info->cat[i].name)+1;
458 for (j = 0; j < name_len; j++)
459 name[j] = tolower(info->cat[i].name[j]);
461 name[name_len-1] = '\0';
462 ret = snprintf(str + offset, rem, "%s|", name+1);
465 OSMO_SNPRINTF_RET(ret, rem, offset, len);
467 offset--; /* to remove the trailing | */
470 ret = snprintf(str + offset, rem, ") (");
473 OSMO_SNPRINTF_RET(ret, rem, offset, len);
475 for (i = 0; i < LOGLEVEL_DEFS; i++) {
476 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
477 char loglevel_str[loglevel_str_len];
479 for (j = 0; j < loglevel_str_len; j++)
480 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
482 loglevel_str[loglevel_str_len-1] = '\0';
483 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
486 OSMO_SNPRINTF_RET(ret, rem, offset, len);
488 offset--; /* to remove the trailing | */
491 ret = snprintf(str + offset, rem, ")");
494 OSMO_SNPRINTF_RET(ret, rem, offset, len);
500 /* This generates the logging command description for VTY. */
501 const char *log_vty_command_description(const struct log_info *info)
504 int i, ret, len = 0, offset = 0, rem;
507 "Set the log level for a specified category\n") + 1;
509 for (i = 0; i < info->num_cat; i++)
510 size += strlen(info->cat[i].description) + 1;
512 for (i = 0; i < LOGLEVEL_DEFS; i++)
513 size += strlen(loglevel_descriptions[i]) + 1;
515 size += strlen("Global setting for all subsystems") + 1;
517 str = talloc_zero_size(tall_log_ctx, size);
521 ret = snprintf(str + offset, rem, LOGGING_STR
522 "Set the log level for a specified category\n");
525 OSMO_SNPRINTF_RET(ret, rem, offset, len);
527 ret = snprintf(str + offset, rem,
528 "Global setting for all subsystems\n");
531 OSMO_SNPRINTF_RET(ret, rem, offset, len);
533 for (i = 0; i < info->num_cat; i++) {
534 ret = snprintf(str + offset, rem, "%s\n",
535 info->cat[i].description);
538 OSMO_SNPRINTF_RET(ret, rem, offset, len);
540 for (i = 0; i < LOGLEVEL_DEFS; i++) {
541 ret = snprintf(str + offset, rem, "%s\n",
542 loglevel_descriptions[i]);
545 OSMO_SNPRINTF_RET(ret, rem, offset, len);
552 void log_init(const struct log_info *cat)
554 tall_log_ctx = talloc_named_const(NULL, 1, "logging");