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;
203 category = &tar->categories[subsys];
204 /* subsystem is not supposed to be logged */
205 if (!category->enabled)
208 /* Check the global log level */
209 if (tar->loglevel != 0 && level < tar->loglevel)
212 /* Check the category log level */
213 if (tar->loglevel == 0 && category->loglevel != 0 &&
214 level < category->loglevel)
217 /* Apply filters here... if that becomes messy we will
218 * need to put filters in a list and each filter will
219 * say stop, continue, output */
220 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
222 else if (osmo_log_info->filter_fn)
223 output = osmo_log_info->filter_fn(&log_context,
228 /* According to the manpage, vsnprintf leaves the value of ap
229 * in undefined state. Since _output uses vsnprintf and it may
230 * be called several times, we have to pass a copy of ap. */
232 _output(tar, subsys, level, file, line, cont, format, ap);
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 generates the logging command string for VTY. */
438 const char *log_vty_command_string(const struct log_info *info)
440 int len = 0, offset = 0, ret, i, rem;
441 int size = strlen("logging level () ()") + 1;
444 for (i = 0; i < info->num_cat; i++)
445 size += strlen(info->cat[i].name) + 1;
447 for (i = 0; i < LOGLEVEL_DEFS; i++)
448 size += strlen(loglevel_strs[i].str) + 1;
451 str = talloc_zero_size(tall_log_ctx, size);
455 ret = snprintf(str + offset, rem, "logging level (all|");
458 OSMO_SNPRINTF_RET(ret, rem, offset, len);
460 for (i = 0; i < info->num_cat; i++) {
461 int j, name_len = strlen(info->cat[i].name)+1;
464 for (j = 0; j < name_len; j++)
465 name[j] = tolower(info->cat[i].name[j]);
467 name[name_len-1] = '\0';
468 ret = snprintf(str + offset, rem, "%s|", name+1);
471 OSMO_SNPRINTF_RET(ret, rem, offset, len);
473 offset--; /* to remove the trailing | */
476 ret = snprintf(str + offset, rem, ") (");
479 OSMO_SNPRINTF_RET(ret, rem, offset, len);
481 for (i = 0; i < LOGLEVEL_DEFS; i++) {
482 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
483 char loglevel_str[loglevel_str_len];
485 for (j = 0; j < loglevel_str_len; j++)
486 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
488 loglevel_str[loglevel_str_len-1] = '\0';
489 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
492 OSMO_SNPRINTF_RET(ret, rem, offset, len);
494 offset--; /* to remove the trailing | */
497 ret = snprintf(str + offset, rem, ")");
500 OSMO_SNPRINTF_RET(ret, rem, offset, len);
506 /* This generates the logging command description for VTY. */
507 const char *log_vty_command_description(const struct log_info *info)
510 int i, ret, len = 0, offset = 0, rem;
513 "Set the log level for a specified category\n") + 1;
515 for (i = 0; i < info->num_cat; i++)
516 size += strlen(info->cat[i].description) + 1;
518 for (i = 0; i < LOGLEVEL_DEFS; i++)
519 size += strlen(loglevel_descriptions[i]) + 1;
521 size += strlen("Global setting for all subsystems") + 1;
523 str = talloc_zero_size(tall_log_ctx, size);
527 ret = snprintf(str + offset, rem, LOGGING_STR
528 "Set the log level for a specified category\n");
531 OSMO_SNPRINTF_RET(ret, rem, offset, len);
533 ret = snprintf(str + offset, rem,
534 "Global setting for all subsystems\n");
537 OSMO_SNPRINTF_RET(ret, rem, offset, len);
539 for (i = 0; i < info->num_cat; i++) {
540 ret = snprintf(str + offset, rem, "%s\n",
541 info->cat[i].description);
544 OSMO_SNPRINTF_RET(ret, rem, offset, len);
546 for (i = 0; i < LOGLEVEL_DEFS; i++) {
547 ret = snprintf(str + offset, rem, "%s\n",
548 loglevel_descriptions[i]);
551 OSMO_SNPRINTF_RET(ret, rem, offset, len);
558 void log_init(const struct log_info *cat)
560 tall_log_ctx = talloc_named_const(NULL, 1, "logging");