LingYun Studio embeded system framwork software, such as thirdparty build shell and lingyun library
guowenxue
2024-08-19 fe321182084c5da0a91fd253e601af41613b94c2
booster/logger.c
@@ -1,416 +1,250 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  logger.c
 *    Description:  This file is the linux infrastructural logger system library
 *
 *        Version:  1.0.0(08/08/2020~)
 *    Description:  This file is common logger API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2020 04:24:01 PM"
 *
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pthread.h>
#include "logger.h"
#define PRECISE_TIME_FACTOR 1000
typedef void (*log_LockFn)(void *udata, int lock);
static unsigned long log_rollback_size = LOG_ROLLBACK_NONE;
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;
/* This library is not thread safe */
static logger_t *logger = NULL;
static const char *level_names[] = {
    "ERROR",
    "WARN",
    "INFO",
    "DEBUG",
    "TRACE"
};
char *log_str[LOG_LEVEL_MAX + 1] = { "", "F", "E", "W", "N", "D", "I", "T", "M" };
static const char *level_colors[] = {
    "\x1b[31m",
    "\x1b[33m",
    "\x1b[32m",
    "\x1b[36m",
    "\x1b[94m"
};
#define LOG_TIME_FMT       "%Y-%m-%d %H:%M:%S"
static void log_signal_handler(int sig)
static inline void time_to_str(char *buf)
{
    if(!logger)
        return ;
    struct timeval   tv;
    struct tm       *tm;
    int              len;
    if (sig == SIGHUP)
    {
        signal(SIGHUP, log_signal_handler);
        log_fatal("SIGHUP received - reopenning log file [%s]", logger->file);
        logger_reopen();
    }
    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 logger_banner(char *prefix)
static void mutex_lock(void *udata, int lock)
{
    if(!logger)
        return ;
    int              err;
    pthread_mutex_t *l = (pthread_mutex_t *) udata;
    fprintf(logger->fp, "%s log \"%s\" on level [%s] size [%lu] KiB, log system version %s\n",
            prefix, logger->file, log_str[logger->level], log_rollback_size / 1024, LOG_VERSION_STR);
#ifdef LOG_FILE_LINE
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ File/Line ]  [ Message ]\n");
#else
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ Message ]\n");
#endif
    fprintf(logger->fp, "-------------------------------------------------------------\n");
}
static void check_and_rollback(void)
{
    if(!logger)
        return ;
    if (log_rollback_size != LOG_ROLLBACK_NONE)
    if (lock)
    {
        long _curOffset = ftell(logger->fp);
        if ((_curOffset != -1) && (_curOffset >= log_rollback_size))
        {
            char cmd[512];
            snprintf(cmd, sizeof(cmd), "cp -f %s %s.roll", logger->file, logger->file);
            system(cmd);
            if (-1 == fseek(logger->fp, 0L, SEEK_SET))
                fprintf(logger->fp, "log rollback fseek failed \n");
            rewind(logger->fp);
            truncate(logger->file, 0);
            logger_banner("Already rollback");
        }
    }
}
/* log_size unit is KB */
int logger_init(logger_t *log, char *log_file, int level, int log_size)
{
    if( !log )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    if( log_file )
    {
        strncpy(log->file, log_file, FILENAME_LEN);
        log->flag |= FLAG_LOGGER_FILE;
        if ( (err = pthread_mutex_lock(l)) != 0 )
            log_error("Unable to lock log lock: %s", strerror(err));
    }
    else
    {
        strncpy(log->file, DBG_LOG_FILE, FILENAME_LEN);
        log->flag |= FLAG_LOGGER_CONSOLE;
        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));
    }
    log->level = level;
    log->size = log_size;
    /* set static global $logger point to argument $log  */
    logger = log;
    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;
}
int logger_open(void)
void log_close(void)
{
    struct sigaction act;
    char *filemode;
    if( L.fp && L.fp!=stderr )
        fclose(L.fp);
    if(!logger)
    {
        printf("ERROR: logger not initialise\n");
        return -1;
    }
    log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024;    /* Unit KiB */
    if ('\0' == logger->file[0])
    {
        printf("ERROR: Logger filename not set\n");
        return -1;
    }
    if (!strcmp(logger->file, DBG_LOG_FILE))
    {
        logger->fp = stderr;
        log_rollback_size = LOG_ROLLBACK_NONE;
        logger->flag |= FLAG_LOGGER_CONSOLE;
        goto OUT;
    }
    filemode = (log_rollback_size==LOG_ROLLBACK_NONE) ? "a+" : "w+";
    logger->fp = fopen(logger->file, filemode);
    if (NULL == logger->fp)
    {
        fprintf(stderr, "Open log file \"%s\" in %s failure: %s\n", logger->file, filemode, strerror(errno));
        return -2;
    }
    act.sa_handler = log_signal_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGHUP, &act, NULL);
  OUT:
    logger_banner("Initialize");
    return 0;
    if (L.udata )
        pthread_mutex_destroy( L.udata);
}
void logger_close(void)
static void log_rollback(void)
{
    if (!logger || !logger->fp )
        return;
    char       cmd[128]={0};
    long       fsize;
    logger_banner("\nTerminate");
    logger_raw("\n\n\n\n");
    /* don't need rollback */
    if(L.size <= 0 )
        return ;
    fflush(logger->fp);
    fsize = ftell(L.fp);
    if( fsize < L.size )
        return ;
    fclose(logger->fp);
    logger->fp = NULL;
    /* 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 ;
}
int logger_reopen(void)
void _log_write(int level, const char *file, int line, const char *fmt, ...)
{
    int rc = 0;
    char *filemode;
    va_list    args;
    char       time_string[100];
    if( !logger )
        return -1;
    log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024;    /* Unit KiB */
    if (logger->flag & FLAG_LOGGER_CONSOLE )
    {
        fflush(logger->fp);
        logger->fp = stderr;
        return 0;
    }
    if (logger->fp)
    {
        logger_close();
        filemode = log_rollback_size==LOG_ROLLBACK_NONE ? "a+" : "w+";
        logger->fp = fopen(logger->file, filemode);
        if (logger->fp == NULL)
            rc = -2;
    }
    else
    {
        rc = -3;
    }
    if (!rc)
    {
        logger_banner("\nReopen");
    }
    return rc;
}
void logger_term(void)
{
    if(!logger)
        return ;
    logger_close();
    logger = NULL;
}
void logger_raw(const char *fmt, ...)
{
    va_list argp;
    if (!logger || !logger->fp)
    if ( !L.fp || level>L.level )
        return;
    check_and_rollback();
    /* Acquire lock */
    if ( L.lockfn )
        L.lockfn(L.udata, 1);
    va_start(argp, fmt);
    vfprintf(logger->fp, fmt, argp);
    va_end(argp);
}
    log_rollback();
static void cp_printout(char *level, char *fmt, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    /* check and rollback file */
    time_to_str(time_string);
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("%s.%03ld [%s] : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
#endif
    if (logger->fp)
        fprintf(logger->fp, "%s.%03ld [%s] : %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
    if (logger->fp)
        fflush(logger->fp);
}
static void cp_printout_line(char *level, char *fmt, char *file, int line, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("[%s.%03ld] <%s> <%s:%04d> : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
#endif
    if (logger->fp)
    /* Log to stderr */
    if ( L.fp == stderr )
    {
        fprintf(logger->fp, "[%s.%03ld] <%s> <%s:%04d> : %s",
                timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
        fflush(logger->fp);
        fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ",
                time_string, level_colors[level], level_names[level], file, line);
    }
}
void logger_str(int level, const char *msg)
{
    if (!logger || level>logger->level)
        return;
    check_and_rollback();
    if (logger->fp)
        fwrite(msg, 1, strlen(msg), logger->fp);
    if(logger->fp)
        fflush(logger->fp);
}
void logger_min(int level, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout(log_str[level], fmt, argp);
    va_end(argp);
}
void logger_line(int level, char *file, int line, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout_line(log_str[level], fmt, file, line, argp);
    va_end(argp);
}
#define LINELEN 81
#define CHARS_PER_LINE 16
static char *print_char =
    "                "
    "                "
    " !\"#$%&'()*+,-./"
    "0123456789:;<=>?"
    "@ABCDEFGHIJKLMNO"
    "PQRSTUVWXYZ[\\]^_"
    "`abcdefghijklmno"
    "pqrstuvwxyz{|}~ "
    "                "
    "                "
    " ???????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????";
void logger_dump(int level, char *buf, int len)
{
    int rc;
    int idx;
    char prn[LINELEN];
    char lit[CHARS_PER_LINE + 2];
    char hc[4];
    short line_done = 1;
    if (!logger || level>logger->level)
        return;
    rc = len;
    idx = 0;
    lit[CHARS_PER_LINE] = '\0';
    while (rc > 0)
    else /* Log to file */
    {
        if (line_done)
            snprintf(prn, LINELEN, "%08X: ", idx);
        fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line);
    }
        do
    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);
}
void log_dump(int level, const char *prompt, char *buf, size_t len)
{
    int                 i, j, ofset;
    char                line[256];
    unsigned char       c;
    unsigned char      *buffer = (unsigned char *)buf;
    if (!L.fp || level>L.level)
        return;
    if( prompt )
        _log_write(level, __FILE__, __LINE__, "%s\r\n", prompt);
    for(i=0; i<len; i+=16)
    {
        ofset = snprintf(line, sizeof(line), "%04x: ", i);
        /* print hex representation, and print spaces if end of buffer */
        for(j=0; j<16; j++)
        {
            unsigned char c = buf[idx];
            snprintf(hc, 4, "%02X ", c);
            strncat(prn, hc, LINELEN);
            lit[idx % CHARS_PER_LINE] = print_char[c];
            if(i+j < len)
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "%02x ", buffer[i+j]);
            else
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "   ");
        }
        while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
        ofset += snprintf(line+ofset, sizeof(line)-ofset, " ");
        line_done = (idx % CHARS_PER_LINE) == 0;
        if (line_done)
        /* print ASCII representation */
        for(j=0; j<16; j++)
        {
#ifdef DUMPLICATE_OUTPUT
            printf("%s  %s\n", prn, lit);
#endif
            if (logger->fp)
                fprintf(logger->fp, "%s  %s\n", prn, lit);
            if (i+j < len)
            {
                c = buffer[i+j];
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "%c", (c>=32 && c<=126) ? c : '.');
            }
            else
            {
                ofset += snprintf(line+ofset, sizeof(line)-ofset, " ");
            }
        }
    }
    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));
#ifdef DUMPLICATE_OUTPUT
        printf("%s  %s\n", prn, lit);
#endif
        if (logger->fp)
            fprintf(logger->fp, "%s  %s\n", prn, lit);
        if (L.fp)
            fprintf(L.fp, "%s\r\n", line);
    }
}