/* logger.c * * Copyright (C) 2013 Tox project All Rights Reserved. * * This file is part of Tox. * * Tox is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Tox 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tox. If not, see . * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "logger.h" #include "crypto_core.h" /* for random_int() */ #include #include #include #include #include #include #include #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) # define getpid() ((unsigned) GetCurrentProcessId()) # define SFILE(FILE__M) (strrchr(FILE__M, '\\') ? strrchr(FILE__M, '\\') + 1 : FILE__M) #else # define SFILE(FILE__M) (strrchr(FILE__M, '/') ? strrchr(FILE__M, '/') + 1 : FILE__M) #endif typedef struct logger { FILE *log_file; LOG_LEVEL level; uint64_t start_time; /* Time when lib loaded */ char* id; /* Allocate these once */ char* tstr; char* posstr; char* msg; /* For thread synchronisation */ pthread_mutex_t mutex[1]; } logger; logger* global = NULL; const char* LOG_LEVEL_STR [] = { [LOG_TRACE] = "TRACE", [LOG_DEBUG] = "DEBUG", [LOG_INFO] = "INFO" , [LOG_WARNING] = "WARN" , [LOG_ERROR] = "ERROR", }; char* strtime(char* dest, size_t max_len) { time_t timer; struct tm *tm_info; time(&timer); tm_info = localtime(&timer); strftime(dest, max_len, "%m:%d %H:%M:%S", tm_info); return dest; } /** * Public Functions */ logger* logger_new (const char *file_name, LOG_LEVEL level, const char* id) { #ifndef LOGGING /* Disabled */ return NULL; #endif logger* retu = calloc(1, sizeof(logger)); if (!retu) return NULL; if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) { free(retu); return NULL; } if (!(retu->log_file = fopen(file_name, "ab"))) { fprintf(stderr, "Error opening logger file: %s; info: %s\n", file_name, strerror(errno)); free(retu); pthread_mutex_destroy(retu->mutex); return NULL; } if (!(retu->tstr = calloc(16, sizeof (char))) || !(retu->posstr = calloc(300, sizeof (char))) || !(retu->msg = calloc(4096, sizeof (char))) ) goto ERROR; if (id) { if (!(retu->id = calloc(strlen(id) + 1, 1))) goto ERROR; strcpy(retu->id, id); } else { if (!(retu->id = malloc(8))) goto ERROR; snprintf(retu->id, 8, "%u", random_int()); } retu->level = level; retu->start_time = current_time_monotonic(); fprintf(retu->log_file, "Successfully created and running logger id: %s; time: %s\n", retu->id, strtime(retu->tstr, 16)); return retu; ERROR: fprintf(stderr, "Failed to create logger!\n"); pthread_mutex_destroy(retu->mutex); fclose(retu->log_file); free(retu->tstr); free(retu->posstr); free(retu->msg); free(retu->id); free(retu); return NULL; } void logger_kill(logger* log) { #ifndef LOGGING /* Disabled */ return; #endif if (!log) return; pthread_mutex_lock(log->mutex); free(log->id); free(log->tstr); free(log->posstr); free(log->msg); if (fclose(log->log_file) != 0 ) perror("Could not close log file"); pthread_mutex_unlock(log->mutex); pthread_mutex_destroy(log->mutex); free(log); } void logger_kill_global(void) { logger_kill(global); } void logger_set_global(logger* log) { #ifndef LOGGING /* Disabled */ return; #endif global = log; } logger* logger_get_global(void) { #ifndef LOGGING /* Disabled */ return NULL; #endif return global; } void logger_write (logger* log, LOG_LEVEL level, const char* file, int line, const char *format, ...) { #ifndef LOGGING /* Disabled */ return; #endif static const char* logger_format = "%s " /* Logger id string */ "%-16s" /* Time string of format: %m:%d %H:%M:%S */ "%u " /* Thread id */ "%-5s " /* Logger lever string */ "%-20s " /* File:line string */ "- %s" /* Output message */ "\n"; /* Every new print new line */ logger* this_log = log ? log: global; if (!this_log) return; /* Don't print levels lesser than set one */ if (this_log->level > level) return; pthread_mutex_lock(this_log->mutex); /* Set position str */ snprintf(this_log->posstr, 300, "%s:%d", SFILE(file), line); /* Set message */ va_list args; va_start (args, format); vsnprintf(this_log->msg, 4096, format, args); va_end (args); fprintf(this_log->log_file, logger_format, this_log->id, strtime(this_log->tstr, 16), pthread_self(), LOG_LEVEL_STR[level], this_log->posstr, this_log->msg); fflush(this_log->log_file); pthread_mutex_unlock(this_log->mutex); }