--- common/log.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- common/log.h | 38 ++++++++-- 2 files changed, 264 insertions(+), 15 deletions(-) diff --git a/common/log.c b/common/log.c index 19a58f0..cfd2ba9 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,20 @@ #include "log.h" #include "backtrace.h" +struct _SpiceLogDomain { + gchar *name; + SpiceLogLevel log_level; + gint ref; +}; + +/* This is our default log variable, in case on is not defined */ +SpiceLogDomain *SPICE_LOG_DOMAIN = NULL; + static int debug_level = -1; static int abort_level = -1; +static GSList *log_domains = NULL; + #ifndef SPICE_ABORT_LEVEL_DEFAULT #ifdef SPICE_DISABLE_ABORT #define SPICE_ABORT_LEVEL_DEFAULT -1 @@ -43,7 +56,8 @@ static int abort_level = -1; static GLogLevelFlags spice_log_level_to_glib(SpiceLogLevel level) { - static GLogLevelFlags glib_levels[] = { + static GLogLevelFlags glib_levels[SPICE_LOG_LEVEL_LAST] = { + [ SPICE_LOG_LEVEL_NONE ] = 0, [ SPICE_LOG_LEVEL_ERROR ] = G_LOG_LEVEL_ERROR, [ SPICE_LOG_LEVEL_CRITICAL ] = G_LOG_LEVEL_CRITICAL, [ SPICE_LOG_LEVEL_WARNING ] = G_LOG_LEVEL_WARNING, @@ -51,7 +65,8 @@ static GLogLevelFlags spice_log_level_to_glib(SpiceLogLevel level) [ SPICE_LOG_LEVEL_INFO ] = G_LOG_LEVEL_INFO, [ SPICE_LOG_LEVEL_DEBUG ] = G_LOG_LEVEL_DEBUG, }; - g_return_val_if_fail ((level >= 0) || (level < G_N_ELEMENTS(glib_levels)), 0); + g_return_val_if_fail (level >= SPICE_LOG_LEVEL_NONE, 0); + g_return_val_if_fail (level < SPICE_LOG_LEVEL_LAST, 0); return glib_levels[level]; } @@ -93,7 +108,203 @@ static void spice_log_set_abort_level(void) } } -static void spice_logv(const char *log_domain, +static gchar *name2level[SPICE_LOG_LEVEL_LAST] = { + "none", "error", "warning", "message", "info", "debug" +}; + +static SpiceLogLevel get_log_level_from_spec(const gchar *level_spec) +{ + guint i; + long int level_num; + char *tail; + + /* "-" or "none" (from name2level) can be used to disable all logging */ + if (strcmp(level_spec, "-") == 0) { + return SPICE_LOG_LEVEL_NONE; + } + + /* '*' means everything */ + if (strcmp(level_spec, "*") == 0) { + return SPICE_LOG_LEVEL_LAST - 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_LEVEL_LAST) + return (SpiceLogLevel) level_num; + + /* match level by name */ + for (i = 0; i < SPICE_LOG_LEVEL_LAST; i++) + if (g_ascii_strcasecmp(level_spec, name2level[i]) == 0) + return i; + + /* If the spec does not match one of our levels, just return the current + * default log level */ + return debug_level; +} + +static SpiceLogDomain *find_domain_by_name (const gchar *name) +{ + GSList *list; + + for (list = log_domains; list != NULL; list = g_slist_next (list)) { + SpiceLogDomain *log_domain = list->data; + + if (g_strcmp0 (log_domain->name, name) == 0) + return log_domain; + } + + return NULL; +} + +static void spice_log_domain_set_level_all (SpiceLogLevel level) +{ + GSList *list; + + debug_level = level; + + for (list = log_domains; list; list = g_slist_next (list)) { + SpiceLogDomain *log_domain = list->data; + log_domain->log_level = level; + } +} + +static gboolean set_debug_from_numeric_value(const gchar *str) +{ + /* Try for backwards compatiblity */ + char *tail; + long int level_num = SPICE_LOG_LEVEL_LAST; + + level_num = strtol(str, &tail, 0); + if (level_num >= SPICE_LOG_LEVEL_NONE + && level_num < SPICE_LOG_LEVEL_LAST) { + spice_log_domain_set_level_all (level_num); + g_debug("all domains are level '%s'", str); + return TRUE; + } + + return FALSE; +} + +static void configure_log_domains(const gchar *domains) +{ + gchar **pairs; + gchar **pair; + + pair = pairs = g_strsplit(domains, ",", 0); + + /* Backwards compatibility */ + if (pair == NULL) { + set_debug_from_numeric_value(domains); + return; + } + + while (*pair) { + SpiceLogDomain *domain; + gchar **pair_info; + + pair_info = g_strsplit(*pair, ":", 2); + if (pair_info[0] && pair_info[1]) { + gchar *domain_spec = pair_info[0]; + gchar *level_spec = pair_info[1]; + SpiceLogLevel level; + + level = get_log_level_from_spec(level_spec); + + if (strcmp(domain_spec, "*") == 0) { + spice_log_domain_set_level_all (level); + g_debug("all domains are level '%s'", level_spec); + g_strfreev (pair_info); + pair++; + continue; + } + + domain = spice_log_domain_new(domain_spec); + domain->log_level = level; + g_debug("domain: '%s', level: '%s'", domain_spec, level_spec); + g_strfreev(pair_info); + } else { + /* Backwards compatibility */ + if (!set_debug_from_numeric_value(*pair)) { + g_debug("invalid log spec: '%s'", *pair); + } + } + pair++; + } + g_strfreev(pairs); +} + +void spice_log_domain_free (SpiceLogDomain *domain) +{ + domain->ref -= 1; + if (domain->ref > 0) { + return; + } + + log_domains = g_slist_remove (log_domains, domain); + g_free (domain->name); + g_slice_free (SpiceLogDomain, domain); + + /* FIXME: We probably need to do the spice_log_domain_cleanup() + * in case all domains created by spice were freed. */ +} + +SpiceLogDomain *spice_log_domain_new (const gchar *name) +{ + SpiceLogDomain *domain; + + if (*name == '\0') { + return SPICE_LOG_DOMAIN; + } + + domain = find_domain_by_name(name); + if (domain == NULL) { + domain = g_slice_new0(SpiceLogDomain); + domain->log_level = debug_level; + domain->name = g_strdup(name); + + log_domains = g_slist_prepend (log_domains, domain); + } + domain->ref += 1; + + return domain; +} + +static void domain_init(void) +{ + const gchar *log_env; + const gchar *messages_env; + + spice_log_set_debug_level(); + spice_log_set_abort_level(); + + /* Set default domain */ + SPICE_LOG_DOMAIN = spice_log_domain_new ("log"); + + log_env = g_getenv("SPICE_DEBUG"); + if (log_env == NULL) { + return; + } + + configure_log_domains(log_env); + + /* 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", G_LOG_DOMAIN, FALSE); + } else if (g_strcmp0 (messages_env, "all") != 0) { + gchar *new_messages_env; + new_messages_env = g_strconcat (messages_env, ":" G_LOG_DOMAIN, NULL); + g_setenv ("G_MESSAGES_DEBUG", new_messages_env, TRUE); + g_free (new_messages_env); + } +} + +static void spice_logv(SpiceLogDomain *domain, SpiceLogLevel log_level, const char *strloc, const char *function, @@ -101,13 +312,11 @@ static void spice_logv(const char *log_domain, va_list args) { GString *log_msg; - static gsize logging_initialized = FALSE; g_return_if_fail(spice_log_level_to_glib(log_level) != 0); - if (g_once_init_enter(&logging_initialized)) { - spice_log_set_debug_level(); - spice_log_set_abort_level(); - g_once_init_leave (&logging_initialized, TRUE); + + if (domain->log_level < log_level) { + return; } log_msg = g_string_new(NULL); @@ -118,7 +327,7 @@ static void spice_logv(const char *log_domain, g_string_append_vprintf(log_msg, format, args); } g_log(G_LOG_DOMAIN, spice_log_level_to_glib(log_level), - "[%s] %s", log_domain, log_msg->str); + "[%s] %s", domain->name, log_msg->str); g_string_free(log_msg, TRUE); if (abort_level != -1 && abort_level >= (int) log_level) { @@ -127,7 +336,7 @@ static void spice_logv(const char *log_domain, } } -void spice_log(const char *log_domain, +void spice_log(SpiceLogDomain *log_domain, SpiceLogLevel log_level, const char *strloc, const char *function, @@ -135,8 +344,20 @@ void spice_log(const char *log_domain, ...) { va_list args; + static GOnce domain_init_once = G_ONCE_INIT; + + g_once (&domain_init_once, (GThreadFunc) domain_init, NULL); + + /* Use default log_domain when one is not set */ + log_domain = (log_domain == NULL) ? SPICE_LOG_DOMAIN : log_domain; va_start (args, format); spice_logv (log_domain, log_level, strloc, function, format, args); va_end (args); } + +void spice_log_domain_cleanup (void) +{ + g_slist_free_full(log_domains, (GDestroyNotify) spice_log_domain_free); + g_clear_pointer(&SPICE_LOG_DOMAIN, (GDestroyNotify) spice_log_domain_free); +} diff --git a/common/log.h b/common/log.h index 8a85369..86d7d5c 100644 --- a/common/log.h +++ b/common/log.h @@ -26,28 +26,56 @@ SPICE_BEGIN_DECLS -#ifndef SPICE_LOG_DOMAIN -#define SPICE_LOG_DOMAIN "Spice" -#endif - #define SPICE_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__) typedef enum { + SPICE_LOG_LEVEL_NONE, SPICE_LOG_LEVEL_ERROR, SPICE_LOG_LEVEL_CRITICAL, SPICE_LOG_LEVEL_WARNING, SPICE_LOG_LEVEL_MESSAGE, SPICE_LOG_LEVEL_INFO, SPICE_LOG_LEVEL_DEBUG, + + SPICE_LOG_LEVEL_LAST } SpiceLogLevel; -void spice_log(const char *log_domain, +/* Opaque */ +typedef struct _SpiceLogDomain SpiceLogDomain; + +/* Default */ +extern SpiceLogDomain *SPICE_LOG_DOMAIN; + + +#define SPICE_LOG_DOMAIN_STATIC(domain) \ + static SpiceLogDomain *domain = NULL + +#define SPICE_LOG_DOMAIN_INIT(domain, name) G_STMT_START { \ + if (domain == NULL) \ + domain = spice_log_domain_new (name); \ +} G_STMT_END + +#define SPICE_LOG_DOMAIN_FINALIZE(domain) G_STMT_START { \ + if (domain != NULL) { \ + spice_log_domain_free (domain); \ + domain = NULL; \ + } \ +} G_STMT_END + +#define SPICE_LOG_DOMAIN_CLEANUP() G_STMT_START { \ + spice_log_domain_cleanup (); \ +} G_STMT_END + +SpiceLogDomain *spice_log_domain_new (const gchar *name); +void spice_log_domain_free (SpiceLogDomain *domain); +void spice_log(SpiceLogDomain *log_domain, SpiceLogLevel log_level, const char *strloc, const char *function, const char *format, ...) SPICE_ATTR_PRINTF(5, 6); +void spice_log_domain_cleanup(void); #define SPICE_LOG(domain, level, format, ...) G_STMT_START { \ spice_log((domain), (level), SPICE_STRLOC, G_STRFUNC, \ -- 2.5.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel