From fc2a14d0f9d07cc300e89fdbf19ad5f49b7aa781 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 1 Oct 2013 11:40:22 -0400 Subject: [PATCH] logging: allow per-domain log level overrides Allow specifying log domains like "DEFAULT,WIFI:DEBUG" to override the log level on a per-domain basis. --- introspection/nm-manager.xml | 7 +- man/NetworkManager.conf.xml | 8 +- man/NetworkManager.xml | 13 ++- src/logging/nm-logging.c | 216 +++++++++++++++++++++++------------ src/logging/nm-logging.h | 10 +- 5 files changed, 166 insertions(+), 88 deletions(-) diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index e076e0516e..2d4cbf8269 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -226,8 +226,11 @@ VPN, SHARING, SUPPLICANT, AGENTS, SETTINGS, SUSPEND, CORE, DEVICE, OLPC, WIMAX, INFINIBAND, FIREWALL, ADSL, BOND, VLAN]. In addition to these domains, the following special domains can be used: [NONE, ALL, - DEFAULT, DHCP, IP]. If an empty string is given, the log level is - changed but the current set of log domains remains unchanged. + DEFAULT, DHCP, IP]. You can also specify that some domains should + log at a different level from the default by appending a colon (':') + and a log level (eg, 'WIFI:DEBUG'). If an empty string is given, the + log level is changed but the current set of log domains remains + unchanged. diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index a07ba6e028..8f7ba6a6e3 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -270,7 +270,8 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth level - One of ERR, + The default logging verbosity level. + One of ERR, WARN, INFO, DEBUG. The ERR level logs only critical errors. WARN logs warnings that may reflect operation. @@ -289,7 +290,10 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth INFINIBAND, FIREWALL, ADSL, BOND, VLAN, BRIDGE, DBUS_PROPS, TEAM, CONCHECK, DCB. In addition, these special domains can be used: NONE, - ALL, DEFAULT, DHCP, IP. + ALL, DEFAULT, DHCP, IP. + You can specify per-domain log level overrides by + adding a colon and a log level to any domain. Eg, + "WIFI:DEBUG". diff --git a/man/NetworkManager.xml b/man/NetworkManager.xml index 8f3713ce34..092375b9fd 100644 --- a/man/NetworkManager.xml +++ b/man/NetworkManager.xml @@ -185,14 +185,19 @@ Sets how much information NetworkManager sends to the log destination (usually syslog's "daemon" facility). By default, only informational, warning, and error - messages are logged. + messages are logged. See the section on logging in + NetworkManager.conf5 + for more information. - A comma-separated list specifying which - operations are logged to the log destination (usually syslog). - By default, most domains are logging-enabled. + + A comma-separated list specifying which operations are logged to the log + destination (usually syslog). By default, most domains are logging-enabled. + See the section on logging in + NetworkManager.conf5 + for more information. diff --git a/src/logging/nm-logging.c b/src/logging/nm-logging.c index e13b50a75c..da49c25064 100644 --- a/src/logging/nm-logging.c +++ b/src/logging/nm-logging.c @@ -54,8 +54,9 @@ nm_log_handler (const gchar *log_domain, #define LOGD_DEFAULT (LOGD_ALL & ~(LOGD_WIFI_SCAN | LOGD_DBUS_PROPS)) -static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR; -static guint64 log_domains = LOGD_DEFAULT; +static guint32 log_level = LOGL_INFO; +static char *log_domains; +static guint64 logging[LOGL_MAX]; static gboolean syslog_opened; typedef struct { @@ -63,12 +64,12 @@ typedef struct { const char *name; } LogDesc; -static const LogDesc level_descs[] = { - { LOGL_ERR, "ERR" }, - { LOGL_WARN | LOGL_ERR, "WARN" }, - { LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" }, - { LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" }, - { 0, NULL } +static const char *level_names[LOGL_MAX] = { + /* Must be in sync with nm-logging.h */ + "DEBUG", + "INFO", + "WARN", + "ERR", }; static const LogDesc domain_descs[] = { @@ -130,95 +131,124 @@ nm_logging_error_quark (void) /************************************************************************/ +static gboolean +match_log_level (const char *level, + guint32 *out_level, + GError **error) +{ + int i; + + for (i = 0; i < LOGL_MAX; i++) { + if (!strcasecmp (level_names[i], level)) { + *out_level = i; + return TRUE; + } + } + + g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_LEVEL, + _("Unknown log level '%s'"), level); + return FALSE; +} + gboolean nm_logging_setup (const char *level, const char *domains, GError **error) { - char **tmp, **iter; - guint64 new_domains = 0; + guint64 new_logging[LOGL_MAX]; + guint32 new_log_level = log_level; + int i; + + if (!domains) + domains = log_domains ? log_domains : "DEFAULT"; + + for (i = 0; i < LOGL_MAX; i++) + new_logging[i] = 0; /* levels */ if (level && strlen (level)) { - gboolean found = FALSE; - const LogDesc *diter; - - for (diter = &level_descs[0]; diter->name; diter++) { - if (!strcasecmp (diter->name, level)) { - log_level = diter->num; - found = TRUE; - break; - } - } - - if (!found) { - g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_LEVEL, - _("Unknown log level '%s'"), level); + if (!match_log_level (level, &new_log_level, error)) return FALSE; - } } /* domains */ if (domains && strlen (domains)) { + char **tmp, **iter; + tmp = g_strsplit_set (domains, ", ", 0); for (iter = tmp; iter && *iter; iter++) { const LogDesc *diter; - gboolean found = FALSE; + guint32 domain_log_level; + guint64 bits; + char *p; if (!strlen (*iter)) continue; - for (diter = &domain_descs[0]; diter->name; diter++) { - if (!strcasecmp (diter->name, *iter)) { - new_domains |= diter->num; - found = TRUE; - break; + p = strchr (*iter, ':'); + if (p) { + *p = '\0'; + if (!match_log_level (p + 1, &domain_log_level, error)) { + g_strfreev (tmp); + return FALSE; + } + } else + domain_log_level = new_log_level; + + bits = 0; + + /* Check for combined domains */ + if (!strcasecmp (*iter, LOGD_ALL_STRING)) + bits = LOGD_ALL; + else if (!strcasecmp (*iter, LOGD_DEFAULT_STRING)) + bits = LOGD_DEFAULT; + else if (!strcasecmp (*iter, LOGD_DHCP_STRING)) + bits = LOGD_DHCP; + else if (!strcasecmp (*iter, LOGD_IP_STRING)) + bits = LOGD_IP; + + /* Check for compatibility domains */ + else if (!strcasecmp (*iter, "HW")) + bits = LOGD_PLATFORM; + + else { + for (diter = &domain_descs[0]; diter->name; diter++) { + if (!strcasecmp (diter->name, *iter)) { + bits = diter->num; + break; + } } } - /* Check for combined domains */ - if (!strcasecmp (*iter, LOGD_ALL_STRING)) { - new_domains = LOGD_ALL; - found = TRUE; - } else if (!strcasecmp (*iter, LOGD_DEFAULT_STRING)) { - new_domains = LOGD_DEFAULT; - found = TRUE; - } else if (!strcasecmp (*iter, LOGD_DHCP_STRING)) { - new_domains |= LOGD_DHCP; - found = TRUE; - } else if (!strcasecmp (*iter, LOGD_IP_STRING)) { - new_domains |= LOGD_IP; - found = TRUE; - } - - /* Check for compatibility domains */ - if (!strcasecmp (*iter, "HW")) { - new_domains |= LOGD_PLATFORM; - found = TRUE; - } - - if (!found) { + if (!bits) { g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_DOMAIN, _("Unknown log domain '%s'"), *iter); return FALSE; } + + for (i = 0; i < domain_log_level; i++) + new_logging[i] &= ~bits; + for (i = domain_log_level; i < LOGL_MAX; i++) + new_logging[i] |= bits; } g_strfreev (tmp); - log_domains = new_domains; + + if (log_domains != (char *)domains) { + g_free (log_domains); + log_domains = g_strdup (domains); + } + + for (i = 0; i < LOGL_MAX; i++) + logging[i] = new_logging[i]; } + log_level = new_log_level; + return TRUE; } const char * nm_logging_level_to_string (void) { - const LogDesc *diter; - - for (diter = &level_descs[0]; diter->name; diter++) { - if (diter->num == log_level) - return diter->name; - } - g_warn_if_reached (); - return ""; + return level_names[log_level]; } const char * @@ -227,13 +257,13 @@ nm_logging_all_levels_to_string (void) static GString *str; if (G_UNLIKELY (!str)) { - const LogDesc *diter; + int i; str = g_string_new (NULL); - for (diter = &level_descs[0]; diter->name; diter++) { + for (i = 0; i < LOGL_MAX; i++) { if (str->len) g_string_append_c (str, ','); - g_string_append (str, diter->name); + g_string_append (str, level_names[i]); } } @@ -245,13 +275,37 @@ nm_logging_domains_to_string (void) { const LogDesc *diter; GString *str; + int i; + + /* We don't just return g_strdup (log_domains) because we want to expand + * "DEFAULT" and "ALL". + */ str = g_string_sized_new (75); for (diter = &domain_descs[0]; diter->name; diter++) { - if (diter->num & log_domains) { - if (str->len) - g_string_append_c (str, ','); - g_string_append (str, diter->name); + /* If it's set for any lower level, it will also be set for LOGL_ERR */ + if (!(diter->num & logging[LOGL_ERR])) + continue; + + if (str->len) + g_string_append_c (str, ','); + g_string_append (str, diter->name); + + /* Check if it's logging at a lower level than the default. */ + for (i = 0; i < log_level; i++) { + if (diter->num & logging[i]) { + g_string_append_printf (str, ":%s", level_names[i]); + break; + } + } + /* Check if it's logging at a higher level than the default. */ + if (!(diter->num & logging[log_level])) { + for (i = log_level + 1; i < LOGL_MAX; i++) { + if (diter->num & logging[i]) { + g_string_append_printf (str, ":%s", level_names[i]); + break; + } + } } } return g_string_free (str, FALSE); @@ -283,7 +337,9 @@ nm_logging_all_domains_to_string (void) gboolean nm_logging_enabled (guint32 level, guint64 domain) { - return !!(log_level & level) && !!(log_domains & domain); + g_return_val_if_fail (level < LOGL_MAX, FALSE); + + return !!(logging[level] & domain); } void @@ -300,29 +356,37 @@ _nm_log (const char *loc, GTimeVal tv; int syslog_level = LOG_INFO; - if (!(log_level & level) || !(log_domains & domain)) + g_return_if_fail (level < LOGL_MAX); + + if (!(logging[level] & domain)) return; va_start (args, fmt); msg = g_strdup_vprintf (fmt, args); va_end (args); - if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG)) { + switch (level) { + case LOGL_DEBUG: g_get_current_time (&tv); syslog_level = LOG_INFO; fullmsg = g_strdup_printf (" [%ld.%ld] [%s] %s(): %s", tv.tv_sec, tv.tv_usec, loc, func, msg); - } else if ((log_level & LOGL_INFO) && (level == LOGL_INFO)) { + break; + case LOGL_INFO: syslog_level = LOG_INFO; fullmsg = g_strconcat (" ", msg, NULL); - } else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) { + break; + case LOGL_WARN: syslog_level = LOG_WARNING; fullmsg = g_strconcat (" ", msg, NULL); - } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) { + break; + case LOGL_ERR: syslog_level = LOG_ERR; g_get_current_time (&tv); fullmsg = g_strdup_printf (" [%ld.%ld] [%s] %s(): %s", tv.tv_sec, tv.tv_usec, loc, func, msg); - } else + break; + default: g_assert_not_reached (); + } if (syslog_opened) syslog (syslog_level, "%s", fullmsg); diff --git a/src/logging/nm-logging.h b/src/logging/nm-logging.h index fed7e07270..34060e9e3b 100644 --- a/src/logging/nm-logging.h +++ b/src/logging/nm-logging.h @@ -70,10 +70,12 @@ enum { /* Log levels */ enum { - LOGL_ERR = 1, - LOGL_WARN = 2, - LOGL_INFO = 3, - LOGL_DEBUG = 4 + LOGL_DEBUG, + LOGL_INFO, + LOGL_WARN, + LOGL_ERR, + + LOGL_MAX }; typedef enum {