/********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio. * All rights reserved. * * Filename: logger.c * Description: This file is common logger API functions * * Version: 1.0.0(11/08/23) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "11/08/23 16:18:43" * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "logger.h" typedef void (*log_LockFn)(void *udata, int lock); static struct { char file[32]; /* logger file name */ FILE *fp; /* logger file pointer */ long size; /* logger file max size */ int level; /* logger level */ log_LockFn lockfn; /* lock function */ void *udata; /* lock data */ } L; static const char *level_names[] = { "ERROR", "WARN", "INFO", "DEBUG", "TRACE" }; static const char *level_colors[] = { "\x1b[31m", "\x1b[33m", "\x1b[32m", "\x1b[36m", "\x1b[94m" }; static inline void time_to_str(char *buf) { struct timeval tv; struct tm *tm; int len; gettimeofday(&tv, NULL); tm = localtime(&tv.tv_sec); len = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec); buf[len] = '\0'; } static void mutex_lock(void *udata, int lock) { int err; pthread_mutex_t *l = (pthread_mutex_t *) udata; if (lock) { if ( (err = pthread_mutex_lock(l)) != 0 ) log_error("Unable to lock log lock: %s", strerror(err)); } else { if ( (err = pthread_mutex_unlock(l)) != 0 ) log_error("Unable to unlock log lock: %s", strerror(err)); } } int log_open(char *fname, int level, int size, int lock) { FILE *fp; L.level = level; L.size = size*1024; if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") ) { strcpy(L.file, "console"); L.fp = stderr; L.size = 0; /* console don't need rollback */ } else { if ( !(fp = fopen(fname, "a+")) ) { fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno)); return -2; } L.fp = fp; strncpy(L.file, fname, sizeof(L.file)); } if( lock ) { static pthread_mutex_t log_lock; pthread_mutex_init(&log_lock, NULL); L.udata = (void *)&log_lock; L.lockfn = mutex_lock; } fprintf(L.fp, "\n"); log_info("logger system(%s) start: file:\"%s\", level:%s, maxsize:%luKiB\n\n", LOG_VERSION, L.file, level_names[level], size); return 0; } void log_close(void) { if( L.fp && L.fp!=stderr ) fclose(L.fp); if (L.udata ) pthread_mutex_destroy( L.udata); } static void log_rollback(void) { char cmd[128]={0}; long fsize; /* don't need rollback */ if(L.size <= 0 ) return ; fsize = ftell(L.fp); if( fsize < L.size ) return ; /* backup current log file */ snprintf(cmd, sizeof(cmd), "cp %s %s.bak", L.file, L.file); system(cmd); /* rollback file */ fseek(L.fp, 0, SEEK_SET); truncate(L.file, 0); fprintf(L.fp, "\n"); log_info("logger system(%s) rollback: file:\"%s\", level:%s, maxsize:%luKiB\n\n", LOG_VERSION, L.file, level_names[L.level], L.size/1024); return ; } void _log_write(int level, const char *file, int line, const char *fmt, ...) { va_list args; char time_string[100]; if ( !L.fp || level>L.level ) return; /* Acquire lock */ if ( L.lockfn ) L.lockfn(L.udata, 1); log_rollback(); /* check and rollback file */ time_to_str(time_string); /* Log to stderr */ if ( L.fp == stderr ) { fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ", time_string, level_colors[level], level_names[level], file, line); } else /* Log to file */ { fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line); } va_start(args, fmt); vfprintf(L.fp, fmt, args); va_end(args); fflush(L.fp); /* Release lock */ if ( L.lockfn ) L.lockfn(L.udata, 0); } #define LINELEN 81 #define CHARS_PER_LINE 16 static char *print_char = " " " " " !\"#$%&'()*+,-./" "0123456789:;<=>?" "@ABCDEFGHIJKLMNO" "PQRSTUVWXYZ[\\]^_" "`abcdefghijklmno" "pqrstuvwxyz{|}~ " " " " " " ???????????????" "????????????????" "????????????????" "????????????????" "????????????????" "????????????????"; void log_dump(int level, const char *prompt, char *buf, size_t len) { int rc; int idx; char prn[LINELEN]; char lit[CHARS_PER_LINE + 2]; char hc[4]; short line_done = 1; if (!L.fp || level>L.level) return; if( prompt ) _log_write(level, __FILE__, __LINE__, "%s", prompt); rc = len; idx = 0; lit[CHARS_PER_LINE] = '\0'; while (rc > 0) { if (line_done) snprintf(prn, LINELEN, "%08X: ", idx); do { unsigned char c = buf[idx]; snprintf(hc, 4, "%02X ", c); strncat(prn, hc, LINELEN); lit[idx % CHARS_PER_LINE] = print_char[c]; } while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); line_done = (idx % CHARS_PER_LINE) == 0; if (line_done) { if (L.fp) fprintf(L.fp, "%s %s\n", prn, lit); } } if (!line_done) { int ldx = idx % CHARS_PER_LINE; lit[ldx++] = print_char[(int)buf[idx]]; lit[ldx] = '\0'; while ((++idx % CHARS_PER_LINE) != 0) strncat(prn, " ", sizeof(prn)-strlen(prn)); if (L.fp) fprintf(L.fp, "%s %s\n", prn, lit); } }