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)
152 /* prepare the data */
158 /* are we using color */
159 if (target->use_color) {
160 const char *c = color(subsys);
162 snprintf(col, sizeof(col), "%s", color(subsys));
163 col[sizeof(col)-1] = '\0';
166 vsnprintf(buf, sizeof(buf), format, ap);
167 buf[sizeof(buf)-1] = '\0';
170 if (target->print_timestamp) {
174 timestr = ctime(&tm);
175 timestr[strlen(timestr)-1] = '\0';
176 snprintf(tim, sizeof(tim), "%s ", timestr);
177 tim[sizeof(tim)-1] = '\0';
179 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
180 sub[sizeof(sub)-1] = '\0';
183 snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
184 target->use_color ? "\033[0;m" : "");
185 final[sizeof(final)-1] = '\0';
186 target->output(target, level, final);
190 static void _logp(unsigned int subsys, int level, char *file, int line,
191 int cont, const char *format, va_list ap)
193 struct log_target *tar;
195 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
196 struct log_category *category;
199 category = &tar->categories[subsys];
200 /* subsystem is not supposed to be logged */
201 if (!category->enabled)
204 /* Check the global log level */
205 if (tar->loglevel != 0 && level < tar->loglevel)
208 /* Check the category log level */
209 if (tar->loglevel == 0 && category->loglevel != 0 &&
210 level < category->loglevel)
213 /* Apply filters here... if that becomes messy we will
214 * need to put filters in a list and each filter will
215 * say stop, continue, output */
216 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
218 else if (osmo_log_info->filter_fn)
219 output = osmo_log_info->filter_fn(&log_context,
223 /* FIXME: copying the va_list is an ugly
224 * workaround against a bug hidden somewhere in
225 * _output. If we do not copy here, the first
226 * call to _output() will corrupt the va_list
227 * contents, and any further _output() calls
228 * with the same va_list will segfault */
231 _output(tar, subsys, level, file, line, cont, format, bp);
237 void logp(unsigned int subsys, char *file, int line, int cont,
238 const char *format, ...)
242 va_start(ap, format);
243 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
247 void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
251 va_start(ap, format);
252 _logp(subsys, level, file, line, cont, format, ap);
256 void log_add_target(struct log_target *target)
258 llist_add_tail(&target->entry, &osmo_log_target_list);
261 void log_del_target(struct log_target *target)
263 llist_del(&target->entry);
266 void log_reset_context(void)
268 memset(&log_context, 0, sizeof(log_context));
271 int log_set_context(uint8_t ctx_nr, void *value)
273 if (ctx_nr > LOG_MAX_CTX)
276 log_context.ctx[ctx_nr] = value;
281 void log_set_all_filter(struct log_target *target, int all)
284 target->filter_map |= LOG_FILTER_ALL;
286 target->filter_map &= ~LOG_FILTER_ALL;
289 void log_set_use_color(struct log_target *target, int use_color)
291 target->use_color = use_color;
294 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
296 target->print_timestamp = print_timestamp;
299 void log_set_log_level(struct log_target *target, int log_level)
301 target->loglevel = log_level;
304 void log_set_category_filter(struct log_target *target, int category,
305 int enable, int level)
307 if (category >= osmo_log_info->num_cat)
309 target->categories[category].enabled = !!enable;
310 target->categories[category].loglevel = level;
313 static void _file_output(struct log_target *target, unsigned int level,
316 fprintf(target->tgt_file.out, "%s", log);
317 fflush(target->tgt_file.out);
320 struct log_target *log_target_create(void)
322 struct log_target *target;
325 target = talloc_zero(tall_log_ctx, struct log_target);
329 INIT_LLIST_HEAD(&target->entry);
331 /* initialize the per-category enabled/loglevel from defaults */
332 for (i = 0; i < osmo_log_info->num_cat; i++) {
333 struct log_category *cat = &target->categories[i];
334 cat->enabled = osmo_log_info->cat[i].enabled;
335 cat->loglevel = osmo_log_info->cat[i].loglevel;
338 /* global settings */
339 target->use_color = 1;
340 target->print_timestamp = 0;
342 /* global log level */
343 target->loglevel = 0;
347 struct log_target *log_target_create_stderr(void)
349 /* since C89/C99 says stderr is a macro, we can safely do this! */
351 struct log_target *target;
353 target = log_target_create();
357 target->type = LOG_TGT_TYPE_STDERR;
358 target->tgt_file.out = stderr;
359 target->output = _file_output;
366 struct log_target *log_target_create_file(const char *fname)
368 struct log_target *target;
370 target = log_target_create();
374 target->type = LOG_TGT_TYPE_FILE;
375 target->tgt_file.out = fopen(fname, "a");
376 if (!target->tgt_file.out)
379 target->output = _file_output;
381 target->tgt_file.fname = talloc_strdup(target, fname);
386 struct log_target *log_target_find(int type, const char *fname)
388 struct log_target *tgt;
390 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
391 if (tgt->type != type)
393 if (tgt->type == LOG_TGT_TYPE_FILE) {
394 if (!strcmp(fname, tgt->tgt_file.fname))
402 void log_target_destroy(struct log_target *target)
405 /* just in case, to make sure we don't have any references */
406 log_del_target(target);
408 if (target->output == &_file_output) {
409 /* since C89/C99 says stderr is a macro, we can safely do this! */
411 /* don't close stderr */
412 if (target->tgt_file.out != stderr)
415 fclose(target->tgt_file.out);
416 target->tgt_file.out = NULL;
423 /* close and re-open a log file (for log file rotation) */
424 int log_target_file_reopen(struct log_target *target)
426 fclose(target->tgt_file.out);
428 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
429 if (!target->tgt_file.out)
432 /* we assume target->output already to be set */
437 /* This can go into some header file so others can benefit from it. */
438 #define SNPRINTF_FAILURE(ret, rem, offset, len) \
447 /* This generates the logging command string for VTY. */
448 const char *log_vty_command_string(const struct log_info *info)
450 int len = 0, offset = 0, ret, i, rem;
451 int size = strlen("logging level () ()") + 1;
454 for (i = 0; i < info->num_cat; i++)
455 size += strlen(info->cat[i].name) + 1;
457 for (i = 0; i < LOGLEVEL_DEFS; i++)
458 size += strlen(loglevel_strs[i].str) + 1;
461 str = talloc_zero_size(NULL, size);
465 ret = snprintf(str + offset, rem, "logging level (");
468 SNPRINTF_FAILURE(ret, rem, offset, len);
470 for (i = 0; i < info->num_cat; i++) {
471 int j, name_len = strlen(info->cat[i].name)+1;
474 for (j = 0; j < name_len; j++)
475 name[j] = tolower(info->cat[i].name[j]);
477 name[name_len-1] = '\0';
478 ret = snprintf(str + offset, rem, "%s|", name+1);
481 SNPRINTF_FAILURE(ret, rem, offset, len);
483 offset--; /* to remove the trailing | */
486 ret = snprintf(str + offset, rem, ") (");
489 SNPRINTF_FAILURE(ret, rem, offset, len);
491 for (i = 0; i < LOGLEVEL_DEFS; i++) {
492 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
493 char loglevel_str[loglevel_str_len];
495 for (j = 0; j < loglevel_str_len; j++)
496 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
498 loglevel_str[loglevel_str_len-1] = '\0';
499 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
502 SNPRINTF_FAILURE(ret, rem, offset, len);
504 offset--; /* to remove the trailing | */
507 ret = snprintf(str + offset, rem, ")");
510 SNPRINTF_FAILURE(ret, rem, offset, len);
515 /* This generates the logging command description for VTY. */
516 const char *log_vty_command_description(const struct log_info *info)
519 int i, ret, len = 0, offset = 0, rem;
522 "Set the log level for a specified category\n") + 1;
524 for (i = 0; i < info->num_cat; i++)
525 size += strlen(info->cat[i].description) + 1;
527 for (i = 0; i < LOGLEVEL_DEFS; i++)
528 size += strlen(loglevel_descriptions[i]) + 1;
531 str = talloc_zero_size(NULL, size);
535 ret = snprintf(str + offset, rem, LOGGING_STR
536 "Set the log level for a specified category\n");
539 SNPRINTF_FAILURE(ret, rem, offset, len);
541 for (i = 0; i < info->num_cat; i++) {
542 ret = snprintf(str + offset, rem, "%s\n",
543 info->cat[i].description);
546 SNPRINTF_FAILURE(ret, rem, offset, len);
548 for (i = 0; i < LOGLEVEL_DEFS; i++) {
549 ret = snprintf(str + offset, rem, "%s\n",
550 loglevel_descriptions[i]);
553 SNPRINTF_FAILURE(ret, rem, offset, len);
559 void log_init(const struct log_info *cat)
561 tall_log_ctx = talloc_named_const(NULL, 1, "logging");