[Spice-devel] [spice-common v3 03/19] log: allow filtering logs with subdomains

Victor Toso victortoso at redhat.com
Sat Mar 12 14:32:04 UTC 2016


Each .c file that want to use spice logging must include common/log.h
and define its subdomain with SPICE_LOG_SUBDOMAIN_STATIC (name) helper.

This static variable is initialized in its first use depending on
SPICE_DEBUG env var.

examples:

- debug level for all subdomains
export SPICE_DEBUG=6
export SPICE_DEBUG=*:debug
export SPICE_DEBUG=*:*

- only debug audio subdomain
export SPICE_DEBUG=*:-,audio:*
export SPICE_DEBUG=*:none,audio:debug

- warn or worse for everything, debug usb
export SPICE_DEBUG=*:warning,usb:*
export SPICE_DEBUG=3,usb:debug
---
 common/log.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 common/log.h |  21 +++++++-
 2 files changed, 173 insertions(+), 12 deletions(-)

diff --git a/common/log.c b/common/log.c
index 9c2f2b2..5dcb4e7 100644
--- a/common/log.c
+++ b/common/log.c
@@ -23,6 +23,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <string.h>
+#include <errno.h>
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif
@@ -30,9 +32,18 @@
 #include "log.h"
 #include "backtrace.h"
 
+const gchar *spice_debug_env = NULL;
+
 static int glib_debug_level = 0;
 static int abort_level = -1;
 
+static gchar *level2name[SPICE_LOG_NUM_LEVELS] = {
+    "none", "error", "critical", "warning", "message", "info", "debug"
+};
+
+#define SPICE_DOMAIN_STR_ALL    "*"
+#define SPICE_DOMAIN_STR_NONE   "-"
+
 #ifndef SPICE_ABORT_LEVEL_DEFAULT
 #ifdef SPICE_DISABLE_ABORT
 #define SPICE_ABORT_LEVEL_DEFAULT -1
@@ -117,33 +128,161 @@ static void spice_log_set_abort_level(void)
     }
 }
 
+static SpiceLogLevel domain_get_level_from_spec_str(const gchar *level_spec)
+{
+    guint i;
+    long int level_num;
+    char *tail;
+
+    /* "-" or "none" (from level2name) can be used to disable all logging */
+    if (strcmp(level_spec, SPICE_DOMAIN_STR_NONE) == 0) {
+        return SPICE_LOG_LEVEL_NONE;
+    }
+
+    /* '*' means everything */
+    if (strcmp(level_spec, SPICE_DOMAIN_STR_ALL) == 0) {
+        return SPICE_LOG_NUM_LEVELS - 1;
+    }
+
+    errno = 0;
+    level_num = strtol(level_spec, &tail, 0);
+    if (!errno &&
+        tail != level_spec &&
+        level_num >= SPICE_LOG_LEVEL_NONE &&
+        level_num < SPICE_LOG_NUM_LEVELS)
+        return (SpiceLogLevel) level_num;
+
+    /* match level by name */
+    for (i = 0; i < SPICE_LOG_NUM_LEVELS; i++)
+        if (g_ascii_strcasecmp(level_spec, level2name[i]) == 0)
+            return i;
+
+    /* If the spec does not match one of our levels, just return the current
+     * default log level */
+    return glib_debug_level;
+}
+
+static SpiceLogLevel domain_get_level_from_numeric_str(const gchar *str)
+{
+    /* Try for backwards compatiblity */
+    char *tail;
+    long int level_num = SPICE_LOG_NUM_LEVELS;
+
+    level_num = strtol(str, &tail, 0);
+    if (level_num >= SPICE_LOG_LEVEL_NONE
+        && level_num < SPICE_LOG_NUM_LEVELS) {
+        return (SpiceLogLevel) level_num;
+    }
+
+    return glib_debug_level;
+}
+
+/* Look by @domain in SPICE_DEBUG and returns its level */
+static SpiceLogLevel domain_get_domain_level_from_env(const gchar *domain_name)
+{
+    gchar **pairs;
+    gchar **pair;
+
+    if (spice_debug_env == NULL || domain_name == NULL) {
+        return glib_debug_level;
+    }
+
+    pair = pairs = g_strsplit(spice_debug_env, ",", 0);
+
+    while (*pair) {
+        SpiceLogSubDomain *domain;
+        gchar **info;
+
+        /* [0] is the domain and [1] the domain's level */
+        info = g_strsplit(*pair, ":", 2);
+        if (info[0] && info[1]) {
+            if (g_strcmp0(domain_name, info[0]) == 0) {
+                SpiceLogLevel level = domain_get_level_from_spec_str(info[1]);
+                g_strfreev(info);
+                g_strfreev(pairs);
+                return level;
+            }
+            g_strfreev(info);
+        } else {
+            if (strcmp(domain_name, SPICE_DOMAIN_STR_ALL) == 0) {
+                /* Backwards compatibility for SPICE_DEBUG=<log-level> */
+                SpiceLogLevel level = domain_get_level_from_numeric_str(*pair);
+                g_strfreev(info);
+                g_strfreev(pairs);
+                return level;
+            }
+            g_strfreev(info);
+        }
+
+        pair++;
+    }
+
+    g_strfreev(pairs);
+    return glib_debug_level;
+}
+
+static void domain_init(void)
+{
+    const gchar *messages_env;
+
+    spice_debug_env = g_getenv("SPICE_DEBUG");
+    if (spice_debug_env == NULL) {
+        return;
+    }
+
+    glib_debug_level = domain_get_domain_level_from_env(SPICE_DOMAIN_STR_ALL);
+
+    /* Add Spice log domain to G_MESSAGES_DEBUG, so the messages are not
+     * filtered by default handler */
+    messages_env = g_getenv ("G_MESSAGES_DEBUG");
+    if (!messages_env) {
+        g_setenv ("G_MESSAGES_DEBUG", SPICE_LOG_DOMAIN, FALSE);
+    } else if (g_strcmp0 (messages_env, "all") != 0) {
+        gchar *new_messages_env;
+        new_messages_env = g_strconcat (messages_env, ":" SPICE_LOG_DOMAIN, NULL);
+        g_setenv ("G_MESSAGES_DEBUG", new_messages_env, TRUE);
+        g_free (new_messages_env);
+    }
+}
+
+static void domain_set_domain_level(SpiceLogSubDomain *domain)
+{
+    if (domain == NULL || domain->initialized == TRUE) {
+        return;
+    }
+
+    domain->initialized = TRUE;
+    domain->log_level = domain_get_domain_level_from_env(domain->name);
+    spice_debug ("Domain: %s, Level: %s", domain->name,
+                 level2name[domain->log_level]);
+}
+
 static void spice_logger(const gchar *log_domain,
                          GLogLevelFlags log_level,
                          const gchar *message,
                          gpointer user_data)
 {
-    if (glib_debug_level != 0) {
-        if ((log_level & G_LOG_LEVEL_MASK) > glib_debug_level)
-            return; // do not print anything
-    }
     g_log_default_handler(log_domain, log_level, message, NULL);
 }
 
-static inline void spice_log_init_once(void)
+static void spice_log_init_once(SpiceLogSubDomain *domain)
 {
     static gsize logging_initialized = FALSE;
 
     if (g_once_init_enter(&logging_initialized)) {
         spice_log_set_debug_level();
         spice_log_set_abort_level();
+        domain_init();
         g_once_init_leave (&logging_initialized, TRUE);
         g_log_set_handler(SPICE_LOG_DOMAIN,
                           G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
                           spice_logger, NULL);
     }
+
+    domain_set_domain_level(domain);
 }
 
-static void spice_logv(const char *log_domain,
+static void spice_logv(SpiceLogSubDomain *domain,
                        SpiceLogLevel log_level,
                        const char *strloc,
                        const char *function,
@@ -152,10 +291,14 @@ static void spice_logv(const char *log_domain,
 {
     GString *log_msg;
 
-    spice_log_init_once();
+    spice_log_init_once(domain);
 
     g_return_if_fail(spice_log_level_to_glib(log_level) != 0);
 
+    if (domain->log_level < log_level) {
+        return;
+    }
+
     log_msg = g_string_new(NULL);
     if (strloc && function) {
         g_string_append_printf(log_msg, "%s:%s: ", strloc, function);
@@ -163,7 +306,8 @@ static void spice_logv(const char *log_domain,
     if (format) {
         g_string_append_vprintf(log_msg, format, args);
     }
-    g_log(log_domain, spice_log_level_to_glib(log_level), "%s", log_msg->str);
+    g_log(SPICE_LOG_DOMAIN, spice_log_level_to_glib(log_level),
+          "[%s] %s", domain->name, log_msg->str);
     g_string_free(log_msg, TRUE);
 
     if (abort_level != -1 && abort_level >= (int) log_level) {
@@ -172,7 +316,7 @@ static void spice_logv(const char *log_domain,
     }
 }
 
-void spice_log(const char *log_domain,
+void spice_log(SpiceLogSubDomain *domain,
                SpiceLogLevel log_level,
                const char *strloc,
                const char *function,
@@ -182,6 +326,6 @@ void spice_log(const char *log_domain,
     va_list args;
 
     va_start (args, format);
-    spice_logv (log_domain, log_level, strloc, function, format, args);
+    spice_logv (domain, log_level, strloc, function, format, args);
     va_end (args);
 }
diff --git a/common/log.h b/common/log.h
index 68f6a24..64e6377 100644
--- a/common/log.h
+++ b/common/log.h
@@ -44,7 +44,24 @@ typedef enum {
     SPICE_LOG_NUM_LEVELS
 } SpiceLogLevel;
 
-void spice_log(const char *log_domain,
+
+typedef struct _SpiceLogSubDomain SpiceLogSubDomain;
+typedef SpiceLogSubDomain *SpiceLogSubDomainPtr;
+
+struct _SpiceLogSubDomain {
+  const gchar *name;
+  SpiceLogLevel log_level;
+  gboolean initialized;
+};
+
+#define SPICE_LOG_DOMAIN_STATIC(n)                                      \
+    static SPICE_GNUC_UNUSED SpiceLogSubDomain SPICE_LOG_SUB_DOMAIN = { \
+        .name = "" n "",                                                \
+        .log_level = SPICE_LOG_LEVEL_ERROR,                             \
+        .initialized = FALSE,                                           \
+    };
+
+void spice_log(SpiceLogSubDomainPtr subdomain,
                SpiceLogLevel log_level,
                const char *strloc,
                const char *function,
@@ -53,7 +70,7 @@ void spice_log(const char *log_domain,
 
 
 #define SPICE_LOG(level, format, ...) G_STMT_START {    \
-    spice_log(SPICE_LOG_DOMAIN,                         \
+    spice_log(&SPICE_LOG_SUB_DOMAIN,                    \
               (level),                                  \
               SPICE_STRLOC,                             \
               G_STRFUNC,                                \
-- 
2.5.0



More information about the Spice-devel mailing list