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 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(NULL, 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);
505 /* This generates the logging command description for VTY. */
506 const char *log_vty_command_description(const struct log_info *info)
509 int i, ret, len = 0, offset = 0, rem;
512 "Set the log level for a specified category\n") + 1;
514 for (i = 0; i < info->num_cat; i++)
515 size += strlen(info->cat[i].description) + 1;
517 for (i = 0; i < LOGLEVEL_DEFS; i++)
518 size += strlen(loglevel_descriptions[i]) + 1;
521 str = talloc_zero_size(NULL, size);
525 ret = snprintf(str + offset, rem, LOGGING_STR
526 "Set the log level for a specified category\n");
529 OSMO_SNPRINTF_RET(ret, rem, offset, len);
531 for (i = 0; i < info->num_cat; i++) {
532 ret = snprintf(str + offset, rem, "%s\n",
533 info->cat[i].description);
536 OSMO_SNPRINTF_RET(ret, rem, offset, len);
538 for (i = 0; i < LOGLEVEL_DEFS; i++) {
539 ret = snprintf(str + offset, rem, "%s\n",
540 loglevel_descriptions[i]);
543 OSMO_SNPRINTF_RET(ret, rem, offset, len);
549 void log_init(const struct log_info *cat)
551 tall_log_ctx = talloc_named_const(NULL, 1, "logging");