From 3ae2758fba1dc9b364238c6e1e7d591b12c3d878 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 26 Mar 2010 21:24:24 +0800 Subject: rename 'debug' interface to 'logging' interface It's not really about debugging, but about generic logging... --- src/logging.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 src/logging.c (limited to 'src/logging.c') diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 00000000..508ccfd3 --- /dev/null +++ b/src/logging.c @@ -0,0 +1,345 @@ +/* Debugging/Logging support code */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2008 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const struct log_info *log_info; + +static struct log_context log_context; +static void *tall_log_ctx = NULL; +static LLIST_HEAD(target_list); + +static const struct value_string loglevel_strs[] = { + { 0, "EVERYTHING" }, + { LOGL_DEBUG, "DEBUG" }, + { LOGL_INFO, "INFO" }, + { LOGL_NOTICE, "NOTICE" }, + { LOGL_ERROR, "ERROR" }, + { LOGL_FATAL, "FATAL" }, + { 0, NULL }, +}; + +int log_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +int log_parse_category(const char *category) +{ + int i; + + for (i = 0; i < log_info->num_cat; ++i) { + if (!strcasecmp(log_info->cat[i].name+1, category)) + return i; + } + + return -EINVAL; +} + +/* + * Parse the category mask. + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void log_parse_category_mask(struct log_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + + category_token = strtok(mask, ":"); + do { + for (i = 0; i < log_info->num_cat; ++i) { + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(log_info->cat[i].name, category_token, + length) == 0) { + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[i].enabled = 1; + target->categories[i].loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + if (subsys < log_info->num_cat) + return log_info->cat[subsys].color; + + return NULL; +} + +static void _output(struct log_target *target, unsigned int subsys, + char *file, int line, int cont, const char *format, + va_list ap) +{ + char col[30]; + char sub[30]; + char tim[30]; + char buf[4096]; + char final[4096]; + + /* prepare the data */ + col[0] = '\0'; + sub[0] = '\0'; + tim[0] = '\0'; + buf[0] = '\0'; + + /* are we using color */ + if (target->use_color) { + const char *c = color(subsys); + if (c) { + snprintf(col, sizeof(col), "%s", color(subsys)); + col[sizeof(col)-1] = '\0'; + } + } + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; + + if (!cont) { + if (target->print_timestamp) { + char *timestr; + time_t tm; + tm = time(NULL); + timestr = ctime(&tm); + timestr[strlen(timestr)-1] = '\0'; + snprintf(tim, sizeof(tim), "%s ", timestr); + tim[sizeof(tim)-1] = '\0'; + } + snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line); + sub[sizeof(sub)-1] = '\0'; + } + + snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf); + final[sizeof(final)-1] = '\0'; + target->output(target, final); +} + + +static void _logp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct log_target *tar; + + llist_for_each_entry(tar, &target_list, entry) { + struct log_category *category; + int output = 0; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be logged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (tar->loglevel == 0 && category->loglevel != 0 && + level < category->loglevel) + continue; + + /* Apply filters here... if that becomes messy we will + * need to put filters in a list and each filter will + * say stop, continue, output */ + if ((tar->filter_map & LOG_FILTER_ALL) != 0) + output = 1; + else if (log_info->filter_fn) + output = log_info->filter_fn(&log_context, + tar); + + if (output) { + /* FIXME: copying the va_list is an ugly + * workaround against a bug hidden somewhere in + * _output. If we do not copy here, the first + * call to _output() will corrupt the va_list + * contents, and any further _output() calls + * with the same va_list will segfault */ + va_list bp; + va_copy(bp, ap); + _output(tar, subsys, file, line, cont, format, bp); + va_end(bp); + } + } +} + +void logp(unsigned int subsys, char *file, int line, int cont, + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _logp(subsys, level, file, line, cont, format, ap); + va_end(ap); +} + +static char hexd_buff[4096]; + +char *hexdump(const unsigned char *buf, int len) +{ + int i; + char *cur = hexd_buff; + + hexd_buff[0] = 0; + for (i = 0; i < len; i++) { + int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); + int rc = snprintf(cur, len_remain, "%02x ", buf[i]); + if (rc <= 0) + break; + cur += rc; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + +void log_add_target(struct log_target *target) +{ + llist_add_tail(&target->entry, &target_list); +} + +void log_del_target(struct log_target *target) +{ + llist_del(&target->entry); +} + +void log_reset_context(void) +{ + memset(&log_context, 0, sizeof(log_context)); +} + +int log_set_context(uint8_t ctx_nr, void *value) +{ + if (ctx_nr > LOG_MAX_CTX) + return -EINVAL; + + log_context.ctx[ctx_nr] = value; + + return 0; +} + +void log_set_all_filter(struct log_target *target, int all) +{ + if (all) + target->filter_map |= LOG_FILTER_ALL; + else + target->filter_map &= ~LOG_FILTER_ALL; +} + +void log_set_use_color(struct log_target *target, int use_color) +{ + target->use_color = use_color; +} + +void log_set_print_timestamp(struct log_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void log_set_log_level(struct log_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void log_set_category_filter(struct log_target *target, int category, + int enable, int level) +{ + if (category >= log_info->num_cat) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _stderr_output(struct log_target *target, const char *log) +{ + fprintf(target->tgt_stdout.out, "%s", log); + fflush(target->tgt_stdout.out); +} + +struct log_target *log_target_create(void) +{ + struct log_target *target; + + target = talloc_zero(tall_log_ctx, struct log_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + memcpy(target->categories, log_info->cat, + sizeof(struct log_category)*log_info->num_cat); + target->use_color = 1; + target->print_timestamp = 0; + target->loglevel = 0; + return target; +} + +struct log_target *log_target_create_stderr(void) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->tgt_stdout.out = stderr; + target->output = _stderr_output; + return target; +} + +void log_init(const struct log_info *cat) +{ + tall_log_ctx = talloc_named_const(NULL, 1, "logging"); + log_info = cat; +} -- cgit v1.2.3