Blame src/grl-log.c

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