logging: allow per-domain log level overrides (bgo #699387)

This commit is contained in:
Dan Winship 2013-12-20 10:05:40 -05:00
commit d430716495
17 changed files with 225 additions and 122 deletions

View file

@ -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.
</tp:docstring>
</arg>
</method>

View file

@ -270,7 +270,8 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
<variablelist>
<varlistentry>
<term><varname>level</varname></term>
<listitem><para>One of <literal>ERR</literal>,
<listitem><para>The default logging verbosity level.
One of <literal>ERR</literal>,
<literal>WARN</literal>, <literal>INFO</literal>,
<literal>DEBUG</literal>. 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.</para>
<para>In addition, these special domains can be used: NONE,
ALL, DEFAULT, DHCP, IP.</para></listitem>
ALL, DEFAULT, DHCP, IP.</para>
<para>You can specify per-domain log level overrides by
adding a colon and a log level to any domain. Eg,
"<literal>WIFI:DEBUG</literal>".</para></listitem>
</varlistentry>
</variablelist>
</para>

View file

@ -185,14 +185,19 @@
<listitem><para>
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 <literal>logging</literal> in
<citerefentry><refentrytitle>NetworkManager.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more information.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--log-domains</option></term>
<listitem><para>A comma-separated list specifying which
operations are logged to the log destination (usually syslog).
By default, most domains are logging-enabled.
<listitem><para>
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 <literal>logging</literal> in
<citerefentry><refentrytitle>NetworkManager.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more information.
</para></listitem>
</varlistentry>
</variablelist>

View file

@ -549,7 +549,7 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team, NMDeviceTeamPrivate *priv)
g_ptr_array_add (argv, (gpointer) config);
}
if (nm_logging_level_enabled (LOGL_DEBUG))
if (nm_logging_enabled (LOGL_DEBUG, LOGD_TEAM))
g_ptr_array_add (argv, (gpointer) "-gg");
g_ptr_array_add (argv, NULL);

View file

@ -1633,7 +1633,7 @@ request_wireless_scan (gpointer user_data)
ssids = build_hidden_probe_list (self);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) {
if (ssids) {
guint i;
char *foo;

View file

@ -2480,7 +2480,7 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason)
argv[i++] = "--script";
argv[i++] = (char *) nm_device_autoipd_helper_path;
if (nm_logging_level_enabled (LOGL_DEBUG))
if (nm_logging_enabled (LOGL_DEBUG, LOGD_AUTOIP4))
argv[i++] = "--debug";
argv[i++] = (char *) nm_device_get_ip_iface (self);
argv[i++] = NULL;
@ -5047,7 +5047,7 @@ spawn_ping (NMDevice *self,
args[6] = str_timeout = g_strdup_printf ("%u", timeout);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, log_domain)) {
cmd = g_strjoinv (" ", (gchar **) args);
nm_log_dbg (log_domain, "(%s): running '%s'",
nm_device_get_iface (self),

View file

@ -431,7 +431,7 @@ get_duid (NMDHCPClient *self)
duid = generate_duid_from_machine_id ();
g_assert (duid);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
escaped = escape_duid (duid);
nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", escaped);
g_free (escaped);
@ -469,7 +469,7 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self,
if (!priv->duid)
priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP)) {
escaped = escape_duid (priv->duid);
nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped);
g_free (escaped);

View file

@ -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,140 @@ nm_logging_error_quark (void)
/************************************************************************/
gboolean
nm_logging_setup (const char *level, const char *domains, GError **error)
static gboolean
match_log_level (const char *level,
guint32 *out_level,
GError **error)
{
char **tmp, **iter;
guint64 new_domains = 0;
int i;
for (i = 0; i < LOGL_MAX; i++) {
if (!g_ascii_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,
char **bad_domains,
GError **error)
{
GString *unrecognized = NULL;
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 (!g_ascii_strcasecmp (*iter, LOGD_ALL_STRING))
bits = LOGD_ALL;
else if (!g_ascii_strcasecmp (*iter, LOGD_DEFAULT_STRING))
bits = LOGD_DEFAULT;
else if (!g_ascii_strcasecmp (*iter, LOGD_DHCP_STRING))
bits = LOGD_DHCP;
else if (!g_ascii_strcasecmp (*iter, LOGD_IP_STRING))
bits = LOGD_IP;
/* Check for compatibility domains */
else if (!g_ascii_strcasecmp (*iter, "HW"))
bits = LOGD_PLATFORM;
else {
for (diter = &domain_descs[0]; diter->name; diter++) {
if (!g_ascii_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;
if (!bits) {
if (!bad_domains) {
g_set_error (error, NM_LOGGING_ERROR, NM_LOGGING_ERROR_UNKNOWN_DOMAIN,
_("Unknown log domain '%s'"), *iter);
return FALSE;
}
if (unrecognized)
g_string_append (unrecognized, ", ");
else
unrecognized = g_string_new (NULL);
g_string_append (unrecognized, *iter);
continue;
}
/* Check for compatibility domains */
if (!strcasecmp (*iter, "HW")) {
new_domains |= LOGD_PLATFORM;
found = TRUE;
}
if (!found) {
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;
if (unrecognized)
*bad_domains = g_string_free (unrecognized, FALSE);
return TRUE;
}
const char *
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 g_strdup (level_names[log_level]);
}
const char *
@ -227,13 +273,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 +291,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);
@ -281,15 +351,11 @@ nm_logging_all_domains_to_string (void)
}
gboolean
nm_logging_level_enabled (guint32 level)
nm_logging_enabled (guint32 level, guint64 domain)
{
return !!(log_level & level);
}
g_return_val_if_fail (level < LOGL_MAX, FALSE);
gboolean
nm_logging_domain_enabled (guint64 domain)
{
return !!(log_domains & domain);
return !!(logging[level] & domain);
}
void
@ -306,29 +372,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 ("<debug> [%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 ("<info> ", msg, NULL);
} else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) {
break;
case LOGL_WARN:
syslog_level = LOG_WARNING;
fullmsg = g_strconcat ("<warn> ", 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 ("<error> [%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);

View file

@ -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 {
@ -107,10 +109,9 @@ void _nm_log (const char *loc,
const char *fmt,
...) __attribute__((__format__ (__printf__, 5, 6)));
const char *nm_logging_level_to_string (void);
char *nm_logging_level_to_string (void);
char *nm_logging_domains_to_string (void);
gboolean nm_logging_level_enabled (guint32 level);
gboolean nm_logging_domain_enabled (guint64 domain);
gboolean nm_logging_enabled (guint32 level, guint64 domain);
const char *nm_logging_all_levels_to_string (void);
const char *nm_logging_all_domains_to_string (void);
@ -126,7 +127,10 @@ const char *nm_logging_all_domains_to_string (void);
#undef nm_error
#undef nm_error_str
gboolean nm_logging_setup (const char *level, const char *domains, GError **error);
gboolean nm_logging_setup (const char *level,
const char *domains,
char **bad_domains,
GError **error);
void nm_logging_syslog_openlog (gboolean debug);
void nm_logging_syslog_closelog (void);

View file

@ -323,6 +323,7 @@ main (int argc, char *argv[])
gs_unref_object NMSessionMonitor *session_monitor = NULL;
GError *error = NULL;
gboolean wrote_pidfile = FALSE;
char *bad_domains = NULL;
GOptionEntry options[] = {
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
@ -405,11 +406,17 @@ main (int argc, char *argv[])
if (!nm_logging_setup (opt_log_level,
opt_log_domains,
&bad_domains,
&error)) {
fprintf (stderr,
_("%s. Please use --help to see a list of valid options.\n"),
error->message);
exit (1);
} else if (bad_domains) {
fprintf (stderr,
_("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
bad_domains);
g_clear_pointer (&bad_domains, g_free);
}
/* When running from the build directory, determine our build directory
@ -470,10 +477,16 @@ main (int argc, char *argv[])
if (opt_log_level == NULL && opt_log_domains == NULL) {
if (!nm_logging_setup (nm_config_get_log_level (config),
nm_config_get_log_domains (config),
&bad_domains,
&error)) {
fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"),
fprintf (stderr, _("Error in configuration file: %s.\n"),
error->message);
exit (1);
} else if (bad_domains) {
fprintf (stderr,
_("Ignoring unrecognized log domain(s) '%s' from config files.\n"),
bad_domains);
g_clear_pointer (&bad_domains, g_free);
}
}

View file

@ -3981,12 +3981,13 @@ impl_manager_set_logging (NMManager *manager,
const char *domains,
GError **error)
{
if (nm_logging_setup (level, domains, error)) {
if (nm_logging_setup (level, domains, NULL, error)) {
char *new_level = nm_logging_level_to_string ();
char *new_domains = nm_logging_domains_to_string ();
nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'",
nm_logging_level_to_string (),
new_domains);
new_level, new_domains);
g_free (new_level);
g_free (new_domains);
return TRUE;
}
@ -3998,8 +3999,8 @@ impl_manager_get_logging (NMManager *manager,
char **level,
char **domains)
{
*level = g_strdup (nm_logging_level_to_string ());
*domains = g_strdup (nm_logging_domains_to_string ());
*level = nm_logging_level_to_string ();
*domains = nm_logging_domains_to_string ();
}
static void

View file

@ -102,7 +102,7 @@ properties_changed (gpointer data)
g_assert (info);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DBUS_PROPS)) {
GString *buf = g_string_new (NULL);
g_hash_table_foreach (info->hash, add_to_string, buf);

View file

@ -12,7 +12,7 @@ main (int argc, char **argv)
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
nm_logging_setup ("debug", NULL, NULL);
nm_logging_setup ("debug", NULL, NULL, NULL);
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
g_assert (argc <= 2);

View file

@ -120,7 +120,7 @@ main (int argc, char **argv)
/* Enable debug messages if called with --debug */
for (; *argv; argv++) {
if (!g_strcmp0 (*argv, "--debug")) {
nm_logging_setup ("debug", NULL, NULL);
nm_logging_setup ("debug", NULL, NULL, NULL);
}
}

View file

@ -831,8 +831,7 @@ create_pppd_cmd_line (NMPPPManager *self,
nm_cmd_line_add_string (cmd, ",");
ppp_debug = !!getenv ("NM_PPP_DEBUG");
if ( nm_logging_level_enabled (LOGL_DEBUG)
&& nm_logging_domain_enabled (LOGD_PPP))
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PPP))
ppp_debug = TRUE;
if (ppp_debug)

View file

@ -21,7 +21,7 @@ main (int argc, char **argv)
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
nm_logging_setup ("debug", NULL, NULL);
nm_logging_setup ("debug", NULL, NULL, NULL);
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
argv++;

View file

@ -911,7 +911,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
if (existing_secrets)
g_hash_table_unref (existing_secrets);
if (nm_logging_level_enabled (LOGL_DEBUG)) {
if (nm_logging_enabled (LOGL_DEBUG, LOGD_SETTINGS)) {
if (hints)
joined_hints = g_strjoinv (",", (char **) hints);
nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets requested flags 0x%X hints '%s'",