#include "flog.h" #include #include #include #include #include #include #include #include #define FLOG_MAX_CATS 32 static char g_path[512]; static long g_rotate_ms = 3600000; static long g_max_bytes = 1 << 20; /* 1 MiB */ static FILE *g_fp = NULL; static long long g_last_rotate_ms = 0; static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct { char cat[32]; long long last_ms; } FlogThrottle; static FlogThrottle g_throttle[FLOG_MAX_CATS]; static int g_num_throttle = 0; static long long now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } int flog_init(const char *path, long rotate_ms, long max_bytes) { pthread_mutex_lock(&g_mutex); if (g_fp) { fclose(g_fp); g_fp = NULL; } g_path[0] = '\0'; if (path && path[0]) { strncpy(g_path, path, sizeof(g_path) - 1); g_fp = fopen(path, "a"); if (!g_fp) { pthread_mutex_unlock(&g_mutex); fprintf(stderr, "flog: cannot open %s\n", path); return -1; } } if (rotate_ms > 0) g_rotate_ms = rotate_ms; if (max_bytes > 0) g_max_bytes = max_bytes; g_last_rotate_ms = now_ms(); pthread_mutex_unlock(&g_mutex); return 0; } void flog_shutdown(void) { pthread_mutex_lock(&g_mutex); if (g_fp) { fclose(g_fp); g_fp = NULL; } pthread_mutex_unlock(&g_mutex); } int flog_enabled(void) { return g_fp != NULL; } void flog(const char *fmt, ...) { if (!g_fp) return; char msg[1024]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); time_t t = time(NULL); struct tm tm; localtime_r(&t, &tm); pthread_mutex_lock(&g_mutex); if (!g_fp) { pthread_mutex_unlock(&g_mutex); return; } fprintf(g_fp, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, msg); fflush(g_fp); pthread_mutex_unlock(&g_mutex); } void flog_throttled(const char *cat, long min_interval_ms, const char *fmt, ...) { if (!g_fp || !cat) return; long long now = now_ms(); int i = 0; pthread_mutex_lock(&g_mutex); for (i = 0; i < g_num_throttle; i++) if (strcmp(g_throttle[i].cat, cat) == 0) break; if (i >= FLOG_MAX_CATS) { pthread_mutex_unlock(&g_mutex); return; } if (i == g_num_throttle) { strncpy(g_throttle[i].cat, cat, sizeof(g_throttle[i].cat) - 1); g_throttle[i].cat[sizeof(g_throttle[i].cat) - 1] = '\0'; g_throttle[i].last_ms = 0; g_num_throttle++; } if (now - g_throttle[i].last_ms < min_interval_ms) { pthread_mutex_unlock(&g_mutex); return; } g_throttle[i].last_ms = now; pthread_mutex_unlock(&g_mutex); char msg[1024]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); time_t t = time(NULL); struct tm tm; localtime_r(&t, &tm); pthread_mutex_lock(&g_mutex); if (!g_fp) { pthread_mutex_unlock(&g_mutex); return; } fprintf(g_fp, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, msg); fflush(g_fp); pthread_mutex_unlock(&g_mutex); } void flog_rotate_check(void) { pthread_mutex_lock(&g_mutex); if (!g_fp) { pthread_mutex_unlock(&g_mutex); return; } long long now = now_ms(); int do_trunc = 0; if (g_rotate_ms > 0 && now - g_last_rotate_ms >= g_rotate_ms) { do_trunc = 1; g_last_rotate_ms = now; } if (g_max_bytes > 0) { struct stat st; if (fstat(fileno(g_fp), &st) == 0 && st.st_size >= g_max_bytes) do_trunc = 1; } if (do_trunc) { fclose(g_fp); g_fp = fopen(g_path, "w"); if (!g_fp) { pthread_mutex_unlock(&g_mutex); fprintf(stderr, "flog: cannot reopen %s\n", g_path); return; } fprintf(g_fp, "--- log cleared at %lld ms ---\n", now); fflush(g_fp); } pthread_mutex_unlock(&g_mutex); }