00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00175 if (filename != NULL)
00176 {
00177 if ((tmp = fopen(filename, mode)) == NULL)
00178 return -1;
00179 }
00180 else
00181 tmp = NULL;
00182
00183
00184 if (log_mutex != NULL || (log_mutex = g_mutex_new()) == NULL)
00185 {
00186 fclose(tmp);
00187 return -3;
00188 }
00189
00190
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
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
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 }