From f6ce0466cdcb331fadb276a0e78df61853c53224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1=D1?= =?UTF-8?q?=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 29 Sep 2011 22:44:44 +0400 Subject: [PATCH] Now loglevels may be set per-component, at runtime * GNUNET_BOTTOM_LOGLEVEL and GNUNET_TOP_LOGLEVEL set global levels Use bottom level to force logging to be more verbose than configured Use top level to force logging to be less verbose than configured Obviously, bottom <= top * GNUNET_LOG sets per-component levels GNUNET_LOG looks like this: name[/bottom[/top]]/... name starts with a non-digit character, must not include '/' bottom and top must consist only of digits, or be empty a description is only used if it matches the component exactly as a special exception (for now) the name '*' matches any component per-component loglevels override global loglevels global levels override whatever is given via arguments or in config Examples: test_client/8/8/ run test_client with DEBUG level (usually leads to a timeout, by the way) */2/2/core/8/8/transport/4/4 run everything with WARNING, core - with DEBUG, transport - with INFO *//1/peerinfo/4/ run everything with top loglevel ERROR, global/configured bottom loglevel, and peerinfo - with bottom loglevel INFO and global/configured top loglevel statistics/ does nothing TODO: * rework the syntax to allow for symbolic loglevel names (make levels mandatory?) * turn _log() into _log_from() calls to get finer separation OR * add a component sublevel and the ability to match it (such as "tcp:connect" or "test_client:verbose") --- src/include/gnunet_common.h | 54 ++++++++++- src/util/common_logging.c | 229 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 260 insertions(+), 23 deletions(-) diff --git a/src/include/gnunet_common.h b/src/include/gnunet_common.h index f484482..c14363f 100644 --- a/src/include/gnunet_common.h +++ b/src/include/gnunet_common.h @@ -161,6 +161,17 @@ typedef void (*GNUNET_Logger) (void *cls, enum GNUNET_ErrorType kind, const char *component, const char *date, const char *message); + +/** + * Minimum log level. + */ +extern enum GNUNET_ErrorType min_level; + +/** + * Number of log calls to ignore. + */ +extern unsigned int skip_log; + /** * Main log function. * @@ -169,8 +180,33 @@ typedef void (*GNUNET_Logger) (void *cls, enum GNUNET_ErrorType kind, * @param ... arguments for format string */ void -GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...); +GNUNET_log_nocheck (enum GNUNET_ErrorType kind, const char *message, ...); + +/* from glib */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _GNUNET_BOOLEAN_EXPR(expr) \ + __extension__ ({ \ + int _gnunet_boolean_var_; \ + if (expr) \ + _gnunet_boolean_var_ = 1; \ + else \ + _gnunet_boolean_var_ = 0; \ + _gnunet_boolean_var_; \ +}) +#define GN_LIKELY(expr) (__builtin_expect (_GNUNET_BOOLEAN_EXPR(expr), 1)) +#define GN_UNLIKELY(expr) (__builtin_expect (_GNUNET_BOOLEAN_EXPR(expr), 0)) +#else +#define GN_LIKELY(expr) (expr) +#define GN_UNLIKELY(expr) (expr) +#endif +#define GNUNET_log(kind,...) do {\ + if (GN_UNLIKELY(skip_log > 0)) {skip_log--;}\ + else {\ + if (GN_UNLIKELY(((kind) & (~GNUNET_ERROR_TYPE_BULK)) <= min_level))\ + GNUNET_log_nocheck (kind, __VA_ARGS__);\ + }\ +} while (0) /** @@ -183,9 +219,23 @@ GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...); * @param ... arguments for format string */ void -GNUNET_log_from (enum GNUNET_ErrorType kind, const char *comp, +GNUNET_log_from_nocheck (enum GNUNET_ErrorType kind, const char *comp, const char *message, ...); +#define GNUNET_log_from(kind,...) \ +do\ +{\ + if (GN_UNLIKELY(skip_log > 0))\ + {\ + skip_log--;\ + }\ + else\ + {\ + if (GN_UNLIKELY(((kind) & (~GNUNET_ERROR_TYPE_BULK)) <= min_level))\ + GNUNET_log_from_nocheck (kind, __VA_ARGS__);\ + }\ +} while (0) + /** * Ignore the next n calls to the log function. diff --git a/src/util/common_logging.c b/src/util/common_logging.c index ff3ac12..4eb67aa 100644 --- a/src/util/common_logging.c +++ b/src/util/common_logging.c @@ -117,7 +117,7 @@ static char *component; /** * Minimum log level. */ -static enum GNUNET_ErrorType min_level; +enum GNUNET_ErrorType min_level; /** * Linked list of our custom loggres. @@ -127,7 +127,7 @@ static struct CustomLogger *loggers; /** * Number of log calls to ignore. */ -static unsigned int skip_log; +unsigned int skip_log; /** * File descriptor to use for "stderr", or NULL for none. @@ -164,6 +164,191 @@ get_type (const char *log) return GNUNET_ERROR_TYPE_INVALID; } +void +process_definition (char *name, const char *comp, int blevel, int tlevel, int *bot, int *top) +{ + if (name[0] != '*' && strcmp (name, comp) != 0) + return; + if (blevel != -1 && blevel > *bot) + *bot = blevel; + if (tlevel != -1 && (tlevel < *top || *top < 0)) + *top = tlevel; +} + + +/** + * Get a string component loglevel definition from the environment, + * and match @comp against it. If it matches, set @bot and @top to + * the values from the definition (or to defaults, if definition omits them). + * Definition format looks like this: + * name[/bottomlevel[/toplevel]]/... + * name must not include slashes, and must begin with a non-digit char. + * name may be * to indicate that this applies to all components + * (there is, however, no wildcard matching algorithm at the moment). + * bottomlevel and toplevel must be valid non-negative integers. + * bottomlevel and toplevel can be zero-length (useful, if you want to + * specify toplevel, but not bottomlevel). + * + * @param comp component name to match + * @param bot pointer to an integer receiving bottom log threshold + * @param top pointer to an integer receiving top log threshold + */ +static void +get_component_log_levels (const char *comp, int *bot, int *top) +{ + const char *logdef; + char *p; + char *start; + short state; + char *tmp; + char *name = NULL; + int blevel, tlevel; + logdef = getenv ("GNUNET_LOG"); + if (logdef == NULL) + return; + tmp = strdup (logdef); + for (p = tmp, state = 0, start = tmp; p[0] != '\0'; p++) + { + if (state == 0) + { + /* beginning of the string - must not be digit, and must not be slash */ + if ((p[0] >= '0' && p[0] <= '9') || (p[0] == '/')) + { + free (tmp); + return; + } + blevel = -1; + tlevel = -1; + state = 1; + continue; + } + if (state == 1) + { + /* within a name, ends with a slash */ + if (p[0] == '/') + { + p[0] = '\0'; + state = 2; + name = start; + start = p + 1; + } + continue; + } + if (state == 2) + { + /* after a name there will be either another name, or bottomlevel */ + if (p[0] >= '0' && p[1] <= '9') + { + /* bottomlevel */ + state = 3; + } + else if (p[0] == '/') + { + blevel = -1; + /* bottomlevel is 0-length, look for toplevel */ + state = 4; + start = p + 1; + } + else + { + state = 0; + start = p; + /* another name starts, process what we've got */ + process_definition (name, comp, blevel, tlevel, bot, top); + p--; + } + continue; + } + if (state == 3) + { + /* within a bottomlevel, consists of digits, ends with a slash */ + if (p[0] == '/') + { + p[0] = '\0'; + if (strlen (start) > 0) + { + errno = 0; + blevel = strtol (start, NULL, 10); + if (errno != 0 || blevel < 0) + { + free (tmp); + return; + } + } + start = p + 1; + /* look for toplevel or for a new name */ + state = 4; + } + else if (p[0] < '0' || p[1] > '9') + { + free (tmp); + return; + } + continue; + } + if (state == 4) + { + /* after a bottomlevel there will be either a new name, or toplevel */ + if (p[0] >= '0' && p[1] <= '9') + { + /* toplevel */ + state = 5; + } + else if (p[0] == '/') + { + tlevel = -1; + /* toplevel is 0-length, look for a new name */ + state = 0; + start = p + 1; + /* process what we've got */ + process_definition (name, comp, blevel, tlevel, bot, top); + } + else + { + state = 0; + start = p; + /* another name starts, process what we've got */ + process_definition (name, comp, blevel, tlevel, bot, top); + p--; + } + continue; + } + if (state == 5) + { + /* within a toplevel, consists of digits, ends with a slash */ + if (p[0] == '/') + { + p[0] = '\0'; + if (strlen (start) > 0) + { + errno = 0; + tlevel = strtol (start, NULL, 10); + if (errno != 0 || tlevel < 0) + { + free (tmp); + return; + } + } + start = p + 1; + /* look for a new name */ + state = 0; + /* another name starts, process what we've got */ + process_definition (name, comp, blevel, tlevel, bot, top); + } + else if (p[0] < '0' || p[1] > '9') + { + free (tmp); + return; + } + continue; + } + free (tmp); + return; + } + if (state == 2) + { + } +} /** * Setup logging. @@ -180,25 +365,32 @@ GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile) int dirwarn; char *fn; const char *env_loglevel; - int env_minlevel = 0; - int env_min_force_level = 100000; + int env_bottom_level = 0; + int env_top_level = 100000; + int comp_bottom_level = -1; + int comp_top_level = -1; #ifdef WINDOWS QueryPerformanceFrequency (&performance_frequency); #endif GNUNET_free_non_null (component); GNUNET_asprintf (&component, "%s-%d", comp, getpid ()); - env_loglevel = getenv ("GNUNET_LOGLEVEL"); + env_loglevel = getenv ("GNUNET_BOTTOM_LOGLEVEL"); if (env_loglevel != NULL) - env_minlevel = get_type (env_loglevel); - env_loglevel = getenv ("GNUNET_FORCE_LOGLEVEL"); + env_bottom_level = get_type (env_loglevel); + env_loglevel = getenv ("GNUNET_TOP_LOGLEVEL"); if (env_loglevel != NULL) - env_min_force_level = get_type (env_loglevel); + env_top_level = get_type (env_loglevel); + get_component_log_levels (comp, &comp_bottom_level, &comp_top_level); + if (comp_bottom_level != -1) + env_bottom_level = comp_bottom_level; + if (comp_top_level != -1) + env_top_level = comp_top_level; min_level = get_type (loglevel); - if (env_minlevel > min_level) - min_level = env_minlevel; - if (env_min_force_level < min_level) - min_level = env_min_force_level; + if (env_bottom_level > min_level) + min_level = env_bottom_level; + if (env_top_level < min_level) + min_level = env_top_level; if (logfile == NULL) return GNUNET_OK; fn = GNUNET_STRINGS_filename_expand (logfile); @@ -383,13 +575,6 @@ mylog (enum GNUNET_ErrorType kind, const char *comp, const char *message, char *buf; va_list vacp; - if (skip_log > 0) - { - skip_log--; - return; - } - if ((kind & (~GNUNET_ERROR_TYPE_BULK)) > min_level) - return; va_copy (vacp, va); size = VSNPRINTF (NULL, 0, message, vacp) + 1; va_end (vacp); @@ -448,7 +633,7 @@ mylog (enum GNUNET_ErrorType kind, const char *comp, const char *message, * @param ... arguments for format string */ void -GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...) +GNUNET_log_nocheck (enum GNUNET_ErrorType kind, const char *message, ...) { va_list va; @@ -468,7 +653,7 @@ GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...) * @param ... arguments for format string */ void -GNUNET_log_from (enum GNUNET_ErrorType kind, const char *comp, +GNUNET_log_from_nocheck (enum GNUNET_ErrorType kind, const char *comp, const char *message, ...) { va_list va; @@ -498,6 +683,8 @@ GNUNET_error_type_to_string (enum GNUNET_ErrorType kind) return _("INFO"); if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0) return _("DEBUG"); + if ((kind & ~GNUNET_ERROR_TYPE_BULK) == 0) + return _("NONE"); return _("INVALID"); } -- 1.7.4