/* * Copyright (C) 2010, 2011 Igalia S.L. * * Contact: Iago Toral Quiroga * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * SECTION:grl-log * @short_description: Log system * * This class stores information related to the log system */ #include "grl-log.h" #include "grl-log-priv.h" #include #include #include #include struct _GrlLogDomain { /*< private >*/ GrlLogLevel log_level; char *name; }; static gchar **grl_log_env; /* 'domain:level' array from GRL_LOG */ static GrlLogLevel grl_default_log_level = GRL_LOG_LEVEL_WARNING; static GSList *log_domains = NULL; /* the list of GrlLogDomain's */ /* Catch all log domain */ /* For clarity, it should not be re-#define'd in this file, code that wants to * log things with log_log_domain should do it explicitly using GRL_LOG(), * instead of using GRL_{DEBUG,INFO,MESSAGE,WARNING,ERROR}() */ GRL_LOG_DOMAIN(GRL_LOG_DOMAIN_DEFAULT); GRL_LOG_DOMAIN(log_log_domain); static GrlLogDomain * grl_log_domain_find_by_name (const gchar *name) { GSList *list; for (list = log_domains; list; list = g_slist_next (list)) { GrlLogDomain *log_domain = list->data; if (g_strcmp0 (log_domain->name, name) == 0) return log_domain; } return NULL; } static GrlLogDomain * _grl_log_domain_new_internal (const gchar *name) { GrlLogDomain *domain; if (*name == '\0' && GRL_LOG_DOMAIN_DEFAULT != NULL) return GRL_LOG_DOMAIN_DEFAULT; domain = g_slice_new (GrlLogDomain); domain->log_level = grl_default_log_level; domain->name = g_strdup (name); log_domains = g_slist_prepend (log_domains, domain); if (*name == '\0' && GRL_LOG_DOMAIN_DEFAULT == NULL) /* Ensure GRL_LOG_DOMAIN_DEFAULT is set. */ GRL_LOG_DOMAIN_DEFAULT = domain; return domain; } /** * grl_log_domain_new: (skip) * @name: The name for the new log domain * * Returns: The new log domain * * Since: 0.1.7 */ GrlLogDomain * grl_log_domain_new (const gchar *name) { GrlLogDomain *domain; gchar **pair; g_return_val_if_fail (name, NULL); domain = _grl_log_domain_new_internal (name); /* If the GRL_LOG env variable contains @name, let's override that domain * verbosity */ if (grl_log_env == NULL) return domain; pair = grl_log_env; while (*pair) { gchar **pair_info; gchar *domain_spec; pair_info = g_strsplit (*pair, ":", 2); domain_spec = pair_info[0]; if (g_strcmp0 (domain_spec, name) == 0) grl_log_configure (*pair); g_strfreev (pair_info); pair++; } return domain; } static void _grl_log_domain_free_internal (GrlLogDomain *domain) { log_domains = g_slist_remove (log_domains, domain); g_free (domain->name); g_slice_free (GrlLogDomain, domain); } /** * grl_log_domain_free: * @domain: a #GrlLogDomain * * Releases @domain. * * Since: 0.1.7 **/ void grl_log_domain_free (GrlLogDomain *domain) { g_return_if_fail (domain); /* domain can actually be GRL_LOG_DOMAIN_DEFAULT if the domain name given * in _new() was "", freeing the default domain is not possible from the * public API */ if (domain == GRL_LOG_DOMAIN_DEFAULT) return; _grl_log_domain_free_internal (domain); } static void grl_log_domain_set_level_all (GrlLogLevel level) { GSList *list; /* Set the default log level to be level, so newly created domains will * have the correct level */ grl_default_log_level = level; for (list = log_domains; list; list = g_slist_next (list)) { GrlLogDomain *log_domain = list->data; log_domain->log_level = level; } } static GrlLogDomain * get_domain_from_spec (const gchar *domain_spec) { GrlLogDomain *domain; domain = grl_log_domain_find_by_name (domain_spec); return domain; } static gchar *name2level[GRL_LOG_LEVEL_LAST] = { "none", "error", "warning", "message", "info", "debug" }; static GrlLogLevel 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 GRL_LOG_LEVEL_NONE; } /* '*' means everything */ if (strcmp (level_spec, "*") == 0) { return GRL_LOG_LEVEL_LAST - 1; } errno = 0; level_num = strtol (level_spec, &tail, 0); if (!errno && tail != level_spec && level_num >= GRL_LOG_LEVEL_NONE && level_num <= GRL_LOG_LEVEL_LAST - 1) return (GrlLogLevel) level_num; for (i = 0; i < GRL_LOG_LEVEL_LAST; i++) if (strcmp (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 grl_default_log_level; } static void configure_log_domains (const gchar *domains) { gchar **pairs; gchar **pair; gchar **pair_info ; gchar *domain_spec; gchar *level_spec; GrlLogDomain *domain; GrlLogLevel level; pair = pairs = g_strsplit (domains, ",", 0); while (*pair) { pair_info = g_strsplit (*pair, ":", 2); if (pair_info[0] && pair_info[1]) { domain_spec = pair_info[0]; level_spec = pair_info[1]; level = get_log_level_from_spec (level_spec); domain = get_domain_from_spec (domain_spec); if (strcmp (domain_spec, "*") == 0) grl_log_domain_set_level_all (level); if (domain == NULL) { g_strfreev (pair_info); pair++; continue; } domain->log_level = level; GRL_LOG (log_log_domain, GRL_LOG_LEVEL_DEBUG, "domain: '%s', level: '%s'", domain_spec, level_spec); g_strfreev (pair_info); } else { GRL_LOG (log_log_domain, GRL_LOG_LEVEL_WARNING, "Invalid log spec: '%s'", *pair); } pair++; } g_strfreev (pairs); } static void grl_log_valist (GrlLogDomain *domain, GrlLogLevel level, const gchar *strloc, const gchar *format, va_list args) { gchar *message; GLogLevelFlags level2flag[GRL_LOG_LEVEL_LAST] = { 0, /* GRL_LOG_LEVEL_NONE */ G_LOG_LEVEL_CRITICAL, /* GRL_LOG_LEVEL_ERROR */ G_LOG_LEVEL_WARNING, /* GRL_LOG_LEVEL_WARNING */ G_LOG_LEVEL_MESSAGE, /* GRL_LOG_LEVEL_MESSAGE */ G_LOG_LEVEL_INFO, /* GRL_LOG_LEVEL_INFO */ G_LOG_LEVEL_DEBUG /* GRL_LOG_LEVEL_DEBUG */ }; g_return_if_fail (domain); g_return_if_fail (level > 0 && level < GRL_LOG_LEVEL_LAST); g_return_if_fail (strloc); g_return_if_fail (format); message = g_strdup_vprintf (format, args); if (level <= domain->log_level) g_log (G_LOG_DOMAIN, level2flag[level], "[%s] %s: %s", domain->name, strloc, message); g_free (message); } /** * grl_log: * @domain: a domain * @level: log level * @strloc: string, usually line of code where function is invoked * @format: log message * @...: parameters to insert in the log message * * Send a log message. * * Since: 0.1.7 **/ void grl_log (GrlLogDomain *domain, GrlLogLevel level, const gchar *strloc, const gchar *format, ...) { va_list var_args; va_start (var_args, format); grl_log_valist (domain, level, strloc, format, var_args); va_end (var_args); } #define DOMAIN_INIT(domain, name) G_STMT_START { \ domain = _grl_log_domain_new_internal (name); \ } G_STMT_END void _grl_log_init_core_domains (void) { const gchar *log_env; const gchar *messages_env; gchar *new_messages_env; DOMAIN_INIT (GRL_LOG_DOMAIN_DEFAULT, ""); DOMAIN_INIT (log_log_domain, "log"); DOMAIN_INIT (config_log_domain, "config"); DOMAIN_INIT (data_log_domain, "data"); DOMAIN_INIT (media_log_domain, "media"); DOMAIN_INIT (plugin_log_domain, "plugin"); DOMAIN_INIT (source_log_domain, "source"); DOMAIN_INIT (multiple_log_domain, "multiple"); DOMAIN_INIT (registry_log_domain, "registry"); /* Retrieve the GRL_DEBUG environment variable, initialize core domains from * it if applicable and keep it for grl_log_domain_new(). Plugins are using * grl_log_domain_new() in their init() functions to initialize their log * domains. At that time, we'll look at the saved GRL_DEBUG to override the * verbosity */ log_env = g_getenv ("GRL_DEBUG"); if (log_env) { /* Add Grilo log domain to G_MESSAGES_DEBUG, so the messages are not filtered by the 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) { 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); } GRL_LOG (log_log_domain, GRL_LOG_LEVEL_DEBUG, "Using log configuration from GRL_DEBUG: %s", log_env); configure_log_domains (log_env); grl_log_env = g_strsplit (log_env, ",", 0); } } #undef DOMAIN_INIT #define DOMAIN_FREE(domain) G_STMT_START { \ _grl_log_domain_free_internal (domain); \ } G_STMT_END void _grl_log_free_core_domains (void) { DOMAIN_FREE (GRL_LOG_DOMAIN_DEFAULT); DOMAIN_FREE (log_log_domain); DOMAIN_FREE (config_log_domain); DOMAIN_FREE (media_log_domain); DOMAIN_FREE (plugin_log_domain); DOMAIN_FREE (source_log_domain); DOMAIN_FREE (multiple_log_domain); DOMAIN_FREE (registry_log_domain); g_strfreev (grl_log_env); } #undef DOMAIN_FREE /** * grl_log_configure: * @config: A string describing the wanted log configuration * * Configure a set of log domains. The default configuration is to display * warning and error messages only for all the log domains. * * The configuration string follows the following grammar: * * |[ * config-list: config | config ',' config-list * config: domain ':' level * domain: '*' | [a-zA-Z0-9]+ * level: '*' | '-' | named-level | num-level * named-level: "none" | "error" | "warning" | "message" | "info" | "debug" * num-level: [0-5] * ]| * * examples: * * "*:*": maximum verbosity for all the log domains * * "*:-": don't print any message * "media-source:debug,metadata-source:debug": prints debug, * info, message warning and error messages for the media-source and * metadata-source log domains * * * It's possible to override the log configuration at runtime by * defining the GRL_DEBUG environment variable to a configuration string * as described above * * Since: 0.1.7 */ void grl_log_configure (const gchar *config) { configure_log_domains (config); }