Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }