Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$

log.c

Go to the documentation of this file.
00001 /*
00002  * Logging mechanisms
00003  * Copyright (c) 2009 Audacious team
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; under version 3 of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses>.
00016  *
00017  * The Audacious team does not consider modular code linking to
00018  * Audacious or using our public API to be a derived work.
00019  */
00020 
00021 #include "log.h"
00022 #include <stdio.h>
00023 #include <string.h>
00024 
00026 #define AUD_LOG_CTIME_FMT   "%c"
00027 
00029 #define AUD_LOG_LTIME_FMT   "%H:%M:%S"
00030 
00035 static gint log_level = AUD_LOG_INFO;
00036 
00038 static FILE *log_file = NULL;
00039 
00041 static GMutex *log_mutex = NULL;
00042 
00044 static GHashTable *log_thread_hash = NULL;
00045 
00047 const gchar *log_level_names[AUD_LOG_ALL] = {
00048     "none",
00049     "FATAL",
00050     "ERROR",
00051     "warning",
00052     "info",
00053     "DEBUG",
00054     "DEBUG+",
00055 };
00056 
00057 
00065 static gchar *
00066 aud_log_timestr(const gchar *fmt)
00067 {
00068     gchar tmp[256] = "";
00069     time_t stamp = time(NULL);
00070     struct tm stamp_tm;
00071 
00072     if (stamp >= 0 && localtime_r(&stamp, &stamp_tm) != NULL)
00073         strftime(tmp, sizeof(tmp), fmt, &stamp_tm);
00074 
00075     return g_strdup(tmp);
00076 }
00077 
00090 static void
00091 aud_log_msg(FILE *f, const gchar *ctx, gint level, const gchar *msg)
00092 {
00093     gchar *timestamp;
00094     GThread *thread = g_thread_self();
00095     gchar *name = (log_thread_hash != NULL) ?
00096         g_hash_table_lookup(log_thread_hash, thread) : NULL;
00097 
00098     timestamp = aud_log_timestr(AUD_LOG_LTIME_FMT);
00099     fprintf(f, "%s <", timestamp);
00100     g_free(timestamp);
00101 
00102     if (name != NULL)
00103     {
00104         if (ctx != NULL)
00105             fprintf(f, "%s|%s", ctx, name);
00106         else
00107             fprintf(f, "%s", name);
00108     }
00109     else
00110     {
00111         fprintf(f, "%s|%p", ctx != NULL ? ctx : "global", (void *) thread);
00112     }
00113 
00114     fprintf(f, "> [%s]: %s", (level >= 0) ? log_level_names[level] :
00115         log_level_names[AUD_LOG_INFO], msg);
00116 
00117     /* A small hack here to ease transition from g_log() etc. */
00118     if (msg[strlen(msg) - 1] != '\n')
00119         fprintf(f, "\n");
00120 
00121     fflush(f);
00122 }
00123 
00124 
00134 static void
00135 aud_do_logv(FILE *f, const gchar *ctx, gint level, const gchar *fmt, va_list args)
00136 {
00137     gchar *msg = g_strdup_vprintf(fmt, args);
00138     aud_log_msg(f, ctx, level, msg);
00139     g_free(msg);
00140 }
00141 
00151 static void
00152 aud_do_log(FILE *f, const gchar *ctx, gint level, const gchar *fmt, ...)
00153 {
00154     va_list ap;
00155 
00156     va_start(ap, fmt);
00157     aud_do_logv(f, ctx, level, fmt, ap);
00158     va_end(ap);
00159 }
00160 
00168 gint
00169 aud_log_init(const gchar *filename, const gchar *mode, gint level)
00170 {
00171     FILE *tmp;
00172     gchar *timestamp;
00173 
00174     /* Open or set logging file descriptor */
00175     if (filename != NULL)
00176     {
00177         if ((tmp = fopen(filename, mode)) == NULL)
00178             return -1;
00179     }
00180     else
00181         tmp = NULL;
00182 
00183     /* Create mutex, unless it already is set */
00184     if (log_mutex != NULL || (log_mutex = g_mutex_new()) == NULL)
00185     {
00186         fclose(tmp);
00187         return -3;
00188     }
00189 
00190     /* Acquire mutex, set log_file etc. */
00191     g_mutex_lock(log_mutex);
00192     if (log_file != NULL)
00193         fclose(log_file);
00194 
00195     if (tmp == NULL)
00196         log_file = stderr;
00197     else
00198         log_file = tmp;
00199 
00200     log_level = level;
00201 
00202     /* Logging starts here */
00203     timestamp = aud_log_timestr(AUD_LOG_CTIME_FMT);
00204     aud_do_log(log_file, NULL, -1, "Logfile opened %s.\n", timestamp);
00205     g_free(timestamp);
00206 
00207     /* Setup thread context hash table */
00208     if (log_thread_hash != NULL)
00209     {
00210         aud_do_log(log_file, NULL, -1, "Warning, log_thread_hash != NULL (%p)!",
00211             log_thread_hash);
00212         g_hash_table_destroy(log_thread_hash);
00213     }
00214 
00215     log_thread_hash = g_hash_table_new_full(
00216         g_direct_hash, g_direct_equal, NULL, g_free);
00217 
00218     g_mutex_unlock(log_mutex);
00219     return 0;
00220 }
00221 
00223 static void
00224 aud_log_print_hash(gpointer key, gpointer value, gpointer found)
00225 {
00226     if (*(gboolean *)found == FALSE)
00227     {
00228         *(gboolean *)found = TRUE;
00229         aud_do_log(log_file, NULL, -1,
00230         "Warning, following lingering log thread contexts found:\n");
00231     }
00232 
00233     aud_do_log(log_file, NULL, -1, " - %p = '%s'\n", key, value);
00234 }
00235 
00240 void
00241 aud_log_close(void)
00242 {
00243     GMutex *tmp;
00244     gchar *timestamp;
00245 
00246     if ((tmp = log_mutex) != NULL)
00247     {
00248         g_mutex_lock(tmp);
00249 
00250         if (log_thread_hash != NULL)
00251         {
00252             gboolean found = FALSE;
00253             g_hash_table_foreach(log_thread_hash,
00254                 aud_log_print_hash, &found);
00255 
00256             g_hash_table_destroy(log_thread_hash);
00257         }
00258         log_thread_hash = NULL;
00259 
00260         timestamp = aud_log_timestr(AUD_LOG_CTIME_FMT);
00261         aud_do_log(log_file, NULL, -1, "Logfile closed %s.\n", timestamp);
00262         g_free(timestamp);
00263 
00264         log_mutex = NULL;
00265 
00266         if (log_file != NULL)
00267             fflush(log_file);
00268 
00269         if (log_file != stderr)
00270             fclose(log_file);
00271 
00272         log_file = NULL;
00273         g_mutex_unlock(tmp);
00274     }
00275 }
00276 
00285 void
00286 aud_log_add_thread_context(GThread *thread, const gchar *name)
00287 {
00288     gchar *tmp = g_strdup(name), *old;
00289     g_mutex_lock(log_mutex);
00290 
00291     old = g_hash_table_lookup(log_thread_hash, thread);
00292     if (old != NULL)
00293         aud_do_log(log_file, NULL, AUD_LOG_INFO,
00294         "Warning, thread %p is already in context ('%s')!\n", thread, old);
00295 
00296     g_hash_table_insert(log_thread_hash, thread, tmp);
00297 
00298     aud_do_log(log_file, NULL, AUD_LOG_INFO,
00299         "Thread %p name set to '%s'\n", thread, name);
00300 
00301     g_mutex_unlock(log_mutex);
00302 }
00303 
00311 void
00312 aud_log_delete_thread_context(GThread *thread)
00313 {
00314     gchar *old;
00315     g_mutex_lock(log_mutex);
00316 
00317     old = g_hash_table_lookup(log_thread_hash, thread);
00318     if (old == NULL)
00319     {
00320         aud_do_log(log_file, NULL, AUD_LOG_INFO,
00321         "Warning, thread %p does not exist in context table!\n", thread);
00322     }
00323     else
00324     {
00325         aud_do_log(log_file, NULL, AUD_LOG_INFO,
00326         "Thread %p name ('%s') deleted from context table.\n", thread, old);
00327         g_hash_table_remove(log_thread_hash, thread);
00328     }
00329 
00330     g_mutex_unlock(log_mutex);
00331 }
00332 
00341 void
00342 aud_logv(const gchar *ctx, gint level, const gchar *fmt, va_list args)
00343 {
00344     if (log_mutex == NULL || log_file == NULL)
00345         aud_do_log(stderr, ctx, level, fmt, args);
00346     else
00347     {
00348         g_mutex_lock(log_mutex);
00349         if (level <= log_level)
00350             aud_do_logv(log_file, ctx, level, fmt, args);
00351         g_mutex_unlock(log_mutex);
00352     }
00353 }
00354 
00363 void
00364 aud_log(const gchar *ctx, gint level, const gchar *fmt, ...)
00365 {
00366     va_list ap;
00367 
00368     va_start(ap, fmt);
00369     aud_logv(ctx, level, fmt, ap);
00370     va_end(ap);
00371 }
00372 
00384 void
00385 aud_log_line(const gchar *ctx, gint level, const gchar *file, const gchar *func,
00386     gint line, const gchar *fmt, ...)
00387 {
00388     gchar *msg, *str, *info = g_strdup_printf("(%s:%s:%d) ", file, func, line);
00389     va_list ap;
00390 
00391     va_start(ap, fmt);
00392     msg = g_strdup_vprintf(fmt, ap);
00393     va_end(ap);
00394 
00395     str = g_strconcat(info, msg, NULL);
00396 
00397     if (log_mutex == NULL || log_file == NULL)
00398         aud_log_msg(stderr, ctx, level, str);
00399     else
00400     {
00401         g_mutex_lock(log_mutex);
00402         aud_log_msg(log_file, ctx, level, str);
00403         g_mutex_unlock(log_mutex);
00404     }
00405 
00406     g_free(info);
00407     g_free(msg);
00408     g_free(str);
00409 }