LCOV - code coverage report
Current view: top level - libemail/src/core - logger.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 70 70
Test Date: 2026-04-15 21:12:52 Functions: 100.0 % 7 7

            Line data    Source code
       1              : #include "logger.h"
       2              : #include "raii.h"
       3              : #include <stdarg.h>
       4              : #include <time.h>
       5              : #include <string.h>
       6              : #include <stdlib.h>
       7              : #include <unistd.h>
       8              : #include <sys/stat.h>
       9              : #include <dirent.h>
      10              : 
      11              : #define MAX_LOG_SIZE (5 * 1024 * 1024) // 5MB
      12              : #define MAX_ROTATED_LOGS 5
      13              : 
      14              : static FILE *g_log_fp = NULL;
      15              : static LogLevel g_log_level = LOG_INFO;
      16              : static char *g_log_path = NULL;
      17              : static int g_log_stderr = 1;
      18              : 
      19              : /** @brief Converts a LogLevel enum to its string representation. */
      20         1792 : static const char* level_to_str(LogLevel level) {
      21         1792 :     switch (level) {
      22         1579 :         case LOG_DEBUG: return "DEBUG";
      23          208 :         case LOG_INFO:  return "INFO";
      24            1 :         case LOG_WARN:  return "WARN";
      25            3 :         case LOG_ERROR: return "ERROR";
      26            1 :         default:        return "UNKNOWN";
      27              :     }
      28              : }
      29              : 
      30              : /**
      31              :  * @brief Rotates log files: session.log → session.log.1, dropping session.log.5.
      32              :  */
      33            1 : static void rotate_logs() {
      34            1 :     if (!g_log_path) return;
      35              : 
      36              :     // session.log.5 -> deleted
      37              :     // session.log.4 -> session.log.5
      38              :     // ...
      39              :     // session.log -> session.log.1
      40              : 
      41            1 :     char old_name[1024], new_name[1024];
      42              : 
      43              :     // Remove the oldest log
      44            1 :     snprintf(old_name, sizeof(old_name), "%s.%d", g_log_path, MAX_ROTATED_LOGS);
      45            1 :     unlink(old_name);
      46              : 
      47              :     // Rotate existing logs
      48            5 :     for (int i = MAX_ROTATED_LOGS - 1; i >= 1; i--) {
      49            4 :         snprintf(old_name, sizeof(old_name), "%s.%d", g_log_path, i);
      50            4 :         snprintf(new_name, sizeof(new_name), "%s.%d", g_log_path, i + 1);
      51            4 :         rename(old_name, new_name);
      52              :     }
      53              : 
      54              :     // Move current log
      55            1 :     snprintf(new_name, sizeof(new_name), "%s.1", g_log_path);
      56            1 :     rename(g_log_path, new_name);
      57              : }
      58              : 
      59           58 : int logger_init(const char *log_file_path, LogLevel level) {
      60           58 :     g_log_level = level;
      61           58 :     g_log_path = strdup(log_file_path);
      62              : 
      63              :     // Check size and rotate if necessary
      64           58 :     struct stat st;
      65           58 :     if (stat(g_log_path, &st) == 0 && st.st_size > MAX_LOG_SIZE) {
      66            1 :         rotate_logs();
      67              :     }
      68              : 
      69           58 :     g_log_fp = fopen(g_log_path, "a");
      70           58 :     if (!g_log_fp) {
      71            1 :         free(g_log_path);
      72            1 :         g_log_path = NULL;
      73            1 :         return -1;
      74              :     }
      75              : 
      76           57 :     logger_log(LOG_INFO, "Logging initialized. Level: %s", level_to_str(level));
      77           57 :     return 0;
      78              : }
      79              : 
      80         1790 : void logger_log(LogLevel level, const char *format, ...) {
      81         1790 :     if (level < g_log_level || !g_log_fp) return;
      82              : 
      83         1735 :     time_t now = time(NULL);
      84         1735 :     struct tm *tm_info = localtime(&now);
      85         1735 :     char time_str[26];
      86         1735 :     strftime(time_str, 26, "%Y-%m-%d %H:%M:%S", tm_info);
      87              : 
      88         1735 :     va_list args;
      89              :     
      90              :     // Log to file
      91         1735 :     fprintf(g_log_fp, "[%s] [%s] ", time_str, level_to_str(level));
      92         1735 :     va_start(args, format);
      93         1735 :     vfprintf(g_log_fp, format, args);
      94         1735 :     va_end(args);
      95         1735 :     fprintf(g_log_fp, "\n");
      96         1735 :     fflush(g_log_fp);
      97              : 
      98              :     // Also log to stderr if ERROR and enabled
      99         1735 :     if (level == LOG_ERROR && g_log_stderr) {
     100            1 :         fprintf(stderr, "ERROR: ");
     101            1 :         va_start(args, format);
     102            1 :         vfprintf(stderr, format, args);
     103            1 :         va_end(args);
     104            1 :         fprintf(stderr, "\n");
     105              :     }
     106              : }
     107              : 
     108           57 : void logger_close(void) {
     109           57 :     if (g_log_fp) {
     110           57 :         fclose(g_log_fp);
     111           57 :         g_log_fp = NULL;
     112              :     }
     113           57 :     if (g_log_path) {
     114           57 :         free(g_log_path);
     115           57 :         g_log_path = NULL;
     116              :     }
     117           57 : }
     118              : 
     119            3 : void logger_set_stderr(int enable) {
     120            3 :     g_log_stderr = enable;
     121            3 : }
     122              : 
     123            2 : int logger_clean_logs(const char *log_dir) {
     124            4 :     RAII_DIR DIR *dir = opendir(log_dir);
     125            2 :     if (!dir) return -1;
     126              : 
     127              :     struct dirent *entry;
     128            5 :     while ((entry = readdir(dir)) != NULL) {
     129            4 :         if (entry->d_type == DT_REG && strstr(entry->d_name, "session.log")) {
     130            1 :             char path[1024];
     131            1 :             snprintf(path, sizeof(path), "%s/%s", log_dir, entry->d_name);
     132            1 :             unlink(path);
     133              :         }
     134              :     }
     135            1 :     return 0;
     136              : }
        

Generated by: LCOV version 2.0-1