#include "logging.h"
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>
#include <unistd.h>
#define LOG_MAX_SIZE (10 * 1024 * 1024)
#define LOG_MAX_ROTATIONS 5
static int g_debug_mode = 0;
static FILE *g_log_file = NULL;
static char g_log_path[512] = "";
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
void logging_set_debug(int enabled) {
g_debug_mode = enabled;
}
int logging_get_debug(void) {
return g_debug_mode;
}
static void rotate_log_file(void) {
if (g_log_path[0] == '\0') return;
if (g_log_file && g_log_file != stdout && g_log_file != stderr) {
fclose(g_log_file);
g_log_file = NULL;
}
char old_path[520], new_path[520];
snprintf(old_path, sizeof(old_path), "%s.%d", g_log_path, LOG_MAX_ROTATIONS);
unlink(old_path);
for (int i = LOG_MAX_ROTATIONS - 1; i >= 1; i--) {
snprintf(old_path, sizeof(old_path), "%s.%d", g_log_path, i);
snprintf(new_path, sizeof(new_path), "%s.%d", g_log_path, i + 1);
rename(old_path, new_path);
}
snprintf(new_path, sizeof(new_path), "%s.1", g_log_path);
rename(g_log_path, new_path);
g_log_file = fopen(g_log_path, "a");
if (!g_log_file) {
g_log_file = stdout;
}
}
static void check_rotation(void) {
if (!g_log_file || g_log_file == stdout || g_log_file == stderr) return;
if (g_log_path[0] == '\0') return;
struct stat st;
if (fstat(fileno(g_log_file), &st) == 0) {
if (st.st_size >= LOG_MAX_SIZE) {
rotate_log_file();
}
}
}
int logging_set_file(const char *path) {
pthread_mutex_lock(&log_mutex);
if (g_log_file && g_log_file != stdout && g_log_file != stderr) {
fclose(g_log_file);
}
g_log_path[0] = '\0';
if (path) {
strncpy(g_log_path, path, sizeof(g_log_path) - 1);
g_log_path[sizeof(g_log_path) - 1] = '\0';
g_log_file = fopen(path, "a");
if (!g_log_file) {
g_log_file = stdout;
g_log_path[0] = '\0';
pthread_mutex_unlock(&log_mutex);
return -1;
}
} else {
g_log_file = stdout;
}
pthread_mutex_unlock(&log_mutex);
return 0;
}
void logging_cleanup(void) {
pthread_mutex_lock(&log_mutex);
if (g_log_file && g_log_file != stdout && g_log_file != stderr) {
fclose(g_log_file);
}
g_log_file = NULL;
g_log_path[0] = '\0';
pthread_mutex_unlock(&log_mutex);
}
static void log_message(const char *level, const char *format, va_list args) {
pthread_mutex_lock(&log_mutex);
check_rotation();
FILE *out = g_log_file ? g_log_file : stdout;
time_t now;
time(&now);
struct tm *local = localtime(&now);
char buf[32];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
fprintf(out, "%s - %-5s - ", buf, level);
vfprintf(out, format, args);
fprintf(out, "\n");
fflush(out);
pthread_mutex_unlock(&log_mutex);
}
void log_error(const char *format, ...) {
va_list args;
va_start(args, format);
int saved_errno = errno;
char msg[1024];
vsnprintf(msg, sizeof(msg), format, args);
va_end(args);
pthread_mutex_lock(&log_mutex);
check_rotation();
FILE *out = g_log_file ? g_log_file : stderr;
time_t now;
time(&now);
struct tm *local = localtime(&now);
char buf[32];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
if (saved_errno != 0) {
fprintf(out, "%s - ERROR - %s: %s\n", buf, msg, strerror(saved_errno));
} else {
fprintf(out, "%s - ERROR - %s\n", buf, msg);
}
fflush(out);
pthread_mutex_unlock(&log_mutex);
}
void log_info(const char *format, ...) {
va_list args;
va_start(args, format);
log_message("INFO", format, args);
va_end(args);
}
void log_debug(const char *format, ...) {
if (!g_debug_mode) return;
va_list args;
va_start(args, format);
log_message("DEBUG", format, args);
va_end(args);
}