/* * debug.c - Common debug support * Copyright (C) 2007 Collabora Ltd. * Copyright (C) 2007 Nokia Corporation * * 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; either * 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:debug * @title: Common debug support * @short_description: API to activate debugging messages from telepathy-glib * * telepathy-glib has an internal mechanism for debug messages and filtering. * Connection managers written with telepathy-glib are expected to connect * this to their own debugging mechanisms: when the CM's debugging mechanism * is activated, it should call tp_debug_set_flags() and/or * tp_debug_set_persistent(). * * The supported debug-mode keywords and the debug messages that they enable * are subject to change, but currently include: * * * misc - low-level utility code * manager - * #TpConnectionManager (client) * connection - #TpBaseConnection (service) * and #TpConnection (client) * contacts - #TpContact objects * (client) * channel - #TpChannel (client) * im - (text) instant messaging * (service) * properties - * TpDBusPropertiesMixin and #TpPropertiesMixin (service) * params - connection manager parameters * (service) * handles - handle reference tracking tracking * in #TpBaseConnection (service) and #TpConnection (client) * accounts - the #TpAccountManager and * #TpAccount objects (client) * contact-lists - the #TpBaseContactList * (service) * debugger - #TpDebugClient objects * tls - #TpTLSCertificate objects * (client) * all - all of the above * */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #define DEBUG_FLAG TP_DEBUG_MISC #include "debug-internal.h" static TpDebugFlags flags = 0; static gboolean tp_debug_persistent = FALSE; /** * tp_debug_set_all_flags: (skip) * * Activate all possible debug modes. This also activates persistent mode, * which should have been orthogonal. * * Deprecated: since 0.6.1. Use tp_debug_set_flags ("all") and * tp_debug_set_persistent() instead. */ void tp_debug_set_all_flags (void) { flags = 0xffff; tp_debug_persistent = TRUE; } static GDebugKey keys[] = { { "misc", TP_DEBUG_MISC }, { "groups", TP_DEBUG_GROUPS }, { "properties", TP_DEBUG_PROPERTIES }, { "connection", TP_DEBUG_CONNECTION }, { "im", TP_DEBUG_IM }, { "params", TP_DEBUG_PARAMS }, { "presence", TP_DEBUG_PRESENCE }, { "manager", TP_DEBUG_MANAGER }, { "channel", TP_DEBUG_CHANNEL }, { "proxy", TP_DEBUG_PROXY }, { "handles", TP_DEBUG_HANDLES }, { "contacts", TP_DEBUG_CONTACTS }, { "accounts", TP_DEBUG_ACCOUNTS }, { "dispatcher", TP_DEBUG_DISPATCHER }, { "client", TP_DEBUG_CLIENT }, { "contact-lists", TP_DEBUG_CONTACT_LISTS }, { "sasl", TP_DEBUG_SASL }, { "room-config", TP_DEBUG_ROOM_CONFIG }, { "call", TP_DEBUG_CALL }, { "debugger", TP_DEBUG_DEBUGGER }, { "tls", TP_DEBUG_TLS }, { 0, } }; typedef struct { guint key; const gchar *domain; } DebugKeyToDomain; /* This is an array of debug key flags to log domains. The point of this is so * that once getting the index of the bit set, _tp_log() can simply index * this array. Aditionally, having the domain already in $domain/$category * format means we don't have to call g_strdup_printf() to get the desired * domain for each debug message logged, and then g_free() to free the newly * created string... */ static DebugKeyToDomain key_to_domain[] = { { TP_DEBUG_MISC, G_LOG_DOMAIN "/misc" }, { TP_DEBUG_GROUPS, G_LOG_DOMAIN "/groups" }, { TP_DEBUG_PROPERTIES, G_LOG_DOMAIN "/properties" }, { TP_DEBUG_IM, G_LOG_DOMAIN "/im" }, { TP_DEBUG_CONNECTION, G_LOG_DOMAIN "/connection" }, { TP_DEBUG_PARAMS, G_LOG_DOMAIN "/params" }, { TP_DEBUG_PRESENCE, G_LOG_DOMAIN "/presence" }, { TP_DEBUG_MANAGER, G_LOG_DOMAIN "/manager" }, { TP_DEBUG_CHANNEL, G_LOG_DOMAIN "/channel" }, { TP_DEBUG_PROXY, G_LOG_DOMAIN "/proxy" }, { TP_DEBUG_HANDLES, G_LOG_DOMAIN "/handles" }, { TP_DEBUG_CONTACTS, G_LOG_DOMAIN "/contacts" }, { TP_DEBUG_ACCOUNTS, G_LOG_DOMAIN "/accounts" }, { TP_DEBUG_DISPATCHER, G_LOG_DOMAIN "/dispatcher" }, { TP_DEBUG_CLIENT, G_LOG_DOMAIN "/client" }, { TP_DEBUG_CONTACT_LISTS, G_LOG_DOMAIN "/contact-lists" }, { TP_DEBUG_SASL, G_LOG_DOMAIN "/sasl" }, { TP_DEBUG_ROOM_CONFIG, G_LOG_DOMAIN "/room-config" }, { TP_DEBUG_DEBUGGER, G_LOG_DOMAIN "/debugger" }, { TP_DEBUG_TLS, G_LOG_DOMAIN "/tls" }, { 0, NULL } }; static GDebugKey persist_keys[] = { { "persist", 1 }, { 0, }, }; /** * tp_debug_set_flags: * @flags_string: The flags to set, comma-separated. If %NULL or empty, * no additional flags are set. * * Set the debug flags indicated by @flags_string, in addition to any already * set. * * The parsing matches that of g_parse_debug_string(). * * If telepathy-glib was compiled with --disable-debug (not recommended), * this function has no practical effect, since the debug messages it would * enable were removed at compile time. * * Since: 0.6.1 */ void tp_debug_set_flags (const gchar *flags_string) { guint nkeys; for (nkeys = 0; keys[nkeys].value; nkeys++); if (flags_string != NULL) _tp_debug_set_flags (g_parse_debug_string (flags_string, keys, nkeys)); } /** * tp_debug_set_flags_from_string: (skip) * @flags_string: The flags to set, comma-separated. If %NULL or empty, * no additional flags are set. * * Set the debug flags indicated by @flags_string, in addition to any already * set. Unlike tp_debug_set_flags(), this enables persistence like * tp_debug_set_persistent() if the "persist" flag is present or the string * is "all" - this turns out to be unhelpful, as persistence should be * orthogonal. * * The parsing matches that of g_parse_debug_string(). * * Deprecated: since 0.6.1. Use tp_debug_set_flags() and * tp_debug_set_persistent() instead */ void tp_debug_set_flags_from_string (const gchar *flags_string) { tp_debug_set_flags (flags_string); if (flags_string != NULL && g_parse_debug_string (flags_string, persist_keys, 1) != 0) tp_debug_set_persistent (TRUE); } /** * tp_debug_set_flags_from_env: (skip) * @var: The name of the environment variable to parse * * Equivalent to * tp_debug_set_flags_from_string (g_getenv (var)), * and has the same problem with persistence being included in "all". * * Deprecated: since 0.6.1. Use tp_debug_set_flags(g_getenv(...)) and * tp_debug_set_persistent() instead */ void tp_debug_set_flags_from_env (const gchar *var) { const gchar *val = g_getenv (var); tp_debug_set_flags (val); if (val != NULL && g_parse_debug_string (val, persist_keys, 1) != 0) tp_debug_set_persistent (TRUE); } /** * tp_debug_set_persistent: * @persistent: TRUE prevents the connection manager mainloop from exiting, * FALSE enables exiting if there are no connections * (the default behavior). * * Used to enable persistent operation of the connection manager process for * debugging purposes. */ void tp_debug_set_persistent (gboolean persistent) { tp_debug_persistent = persistent; } /* * _tp_debug_set_flags: * @new_flags More flags to set * * Set extra flags. For internal use only */ void _tp_debug_set_flags (TpDebugFlags new_flags) { flags |= new_flags; } /* * _tp_debug_set_flags: * @flag: Flag to test * * Returns: %TRUE if the flag is set. For use via DEBUGGING() only. */ gboolean _tp_debug_flag_is_set (TpDebugFlags flag) { return (flag & flags) != 0; } static const gchar * debug_flag_to_domain (TpDebugFlags flag) { gint index, max; /* First bit set of @flag. This to make sure we only have one bit (in the * unlikely scenario that multiple debug flags were set). This enables us to * index the #key_to_domain array, instead of having to iterate it looking * for the right key. */ index = g_bit_nth_lsf (flag, -1); /* The maximum valid index of the #key_to_domain array. Decrement it by one * because there is the blank { 0, NULL } item on the end which we want to * ignore. */ max = G_N_ELEMENTS (key_to_domain) - 1; /* If the index we got isn't valid, just return "misc". */ if (index < 0 || index >= max) return G_LOG_DOMAIN "/misc"; else return key_to_domain[index].domain; } /* * _tp_log: * @level: Log level * @flag: Debug flag * @format: Format string for g_logv * * Emit a debug message with the given format and arguments, but only * if the given debug flag is set. For use via * ERROR()/CRITICAL()/.../DEBUG() only. */ void _tp_log (GLogLevelFlags level, TpDebugFlags flag, const gchar *format, ...) { if ((flag & flags) || level > G_LOG_LEVEL_DEBUG) { va_list args; va_start (args, format); g_logv (debug_flag_to_domain (flag), level, format, args); va_end (args); } } /* * _tp_debug_is_persistent: * * Returns: %TRUE if persistent mainloop behavior has been enabled with * tp_debug_set_persistent(). */ gboolean _tp_debug_is_persistent (void) { return tp_debug_persistent; } /** * tp_debug_divert_messages: * @filename: A file to which to divert stdout and stderr, or %NULL to * do nothing * * Open the given file for writing and duplicate its file descriptor to * be used for stdout and stderr. This has the effect of closing the previous * stdout and stderr, and sending all messages that would have gone there * to the given file instead. * * By default the file is truncated and hence overwritten each time the * process is executed. * Since version 0.7.14, if the filename is prefixed with '+' then the * file is not truncated and output is added at the end of the file. * * Passing %NULL to this function is guaranteed to have no effect. This is * so you can call it with the recommended usage * tp_debug_divert_messages (g_getenv ("MYAPP_LOGFILE")) * and it won't do anything if the environment variable is not set. * * This function still works if telepathy-glib was compiled without debug * support. * * Since: 0.7.1 */ void tp_debug_divert_messages (const gchar *filename) { int fd; if (filename == NULL) return; if (filename[0] == '+') { /* open in append mode */ fd = g_open (filename + 1, O_WRONLY | O_CREAT | O_APPEND, 0644); } else { /* open in trunc mode */ fd = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); } if (fd == -1) { WARNING ("Can't open logfile '%s': %s", filename, g_strerror (errno)); return; } if (dup2 (fd, 1) == -1) /* STDOUT_FILENO is less universal */ { WARNING ("Error duplicating stdout file descriptor: %s", g_strerror (errno)); return; } if (dup2 (fd, 2) == -1) /* STDERR_FILENO is less universal */ { WARNING ("Error duplicating stderr file descriptor: %s", g_strerror (errno)); } /* avoid leaking the fd */ if (close (fd) != 0) { WARNING ("Error closing temporary logfile fd: %s", g_strerror (errno)); } } /** * tp_debug_timestamped_log_handler: * @log_domain: the message's log domain * @log_level: the log level of the message * @message: the message to process * @ignored: not used * * A #GLogFunc that prepends the UTC time (currently in ISO 8601 format, * with microsecond resolution) to the message, then calls * g_log_default_handler. * * Intended usage is: * * if (g_getenv ("MYPROG_TIMING") != NULL) * g_log_set_default_handler (tp_debug_timestamped_log_handler, NULL); * * * If telepathy-glib was compiled with --disable-debug (not recommended), * this function is equivalent to g_log_default_handler(). * * Changed in 0.9.0: timestamps are now printed in UTC, in * RFC-3339 format. Previously, they were printed in local time, in a * format similar to RFC-3339. * * Since: 0.7.1 */ void tp_debug_timestamped_log_handler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer ignored) { #ifdef ENABLE_DEBUG GTimeVal now; gchar *tmp, *now_str; g_get_current_time (&now); now_str = g_time_val_to_iso8601 (&now); tmp = g_strdup_printf ("%s: %s", now_str, message); g_free (now_str); message = tmp; #endif g_log_default_handler (log_domain, log_level, message, NULL); #ifdef ENABLE_DEBUG g_free (tmp); #endif }