日常工作中常常需要输出日志进行调试,本文提供了一个 C 语言编写的日志框架。可以输出日志到文件或者是终端,支持日志等级设置,不同日志等级可以设置不同的颜色,另外还可以设置进程名、模块名、函数名等的输出,能够打印毫秒级时间,能够限制文件大小,使用方便,架构小巧灵活。本架构多线程的支持有待验证,每次日志调用都会使用写入,性能有待验证,另外,写满切换文件没有实现。

# SimpleLog.h

/*****************************************************************************
simple log frame for test
*****************************************************************************/
#ifndef _SIMPLE_LOG_H_
#define _SIMPLE_LOG_H_
#include <stdbool.h>
#define COLOR_CTRL_CODE "\033[0;"
#define COLOR_CTRL_CLOSE "\033[0m"
#define F_RED           "31m"
#define F_GREEN         "32m"
#define F_YELLOW        "33m"
#define F_NONE          "0m"
#define COLOR_ERROR     F_RED       // 错误
#define COLOR_WARN      F_YELLOW    // 警告
#define COLOR_INFO      F_GREEN     // 信息
#define COLOR_DEBUG     F_NONE      // 调试
// 日志级别
#define LOG_LEVEL_ERROR     0  // 错误
#define LOG_LEVEL_WARN      1  // 警告
#define LOG_LEVEL_INFO      2  // 信息
#define LOG_LEVEL_DEBUG     3  // 调试
#define LOG_LEVEL_MAX LOG_LEVEL_DEBUG
// 日志输出目标
#define LOG_TARGET_TERM 0  // 输出到终端
#define LOG_TARGET_FILE 1  // 输出到文件
bool SlogInit(int _iTarget, int _iLevel, const char *_strPath);
bool SlogDeinit();
bool SlogSetLevel(int _iLevel);
bool SlogGetLevel(int *_iLevel); 
void Slog(int _iLevel, const char* format, ...);
// 重定位日志接口
#define LOG_MAIN "logtest" // 进程名
#define LOG_SUB "test" // 模块名
#define Sdebug(arg, vl...)  \
    Slog(LOG_LEVEL_DEBUG, "[%s][%s][%s]" COLOR_CTRL_CODE COLOR_DEBUG arg COLOR_CTRL_CLOSE, LOG_MAIN, LOG_SUB, __FUNCTION__, ##vl)
#define Sinfo(arg, vl...)   \
    Slog(LOG_LEVEL_INFO, "[%s][%s][%s]" COLOR_CTRL_CODE COLOR_INFO arg COLOR_CTRL_CLOSE, LOG_MAIN, LOG_SUB, __FUNCTION__, ##vl)
#define Swarn(arg, vl...)   \
    Slog(LOG_LEVEL_WARN, "[%s][%s][%s]" COLOR_CTRL_CODE COLOR_WARN arg COLOR_CTRL_CLOSE, LOG_MAIN, LOG_SUB, __FUNCTION__, ##vl)
#define Serror(arg, vl...)  \
    Slog(LOG_LEVEL_ERROR, "[%s][%s][%s]" COLOR_CTRL_CODE COLOR_ERROR arg COLOR_CTRL_CLOSE, LOG_MAIN, LOG_SUB, __FUNCTION__, ##vl)
#endif //_SIMPLE_LOG_H_

# SimpleLog.c

/*****************************************************************************
simple log frame for test
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include "SimpleLog.h"
#define LOG_DEFAULT_PATH "./test.log"
#define LOG_MAX_SZIE (10 * 1024 * 1024)  // M
char *g_strLevel[] = {
    "error", "warn ", "info ", "debug"
};
typedef struct _TSimpleLog_ {
    FILE *fp;
    int iTarget;
    int iLevel;
    int iSize;
    char strPath[256];
}TSimpleLog;
// 日志全局信息
static TSimpleLog g_tLog = {0};
bool TimeToString(const time_t _time, char *_strTime, int iLen) {
    struct tm *p = NULL;
    p = localtime(&_time);
    p->tm_year = p->tm_year + 1900;
    p->tm_mon = p->tm_mon + 1;
 
    snprintf(_strTime, iLen, "%04d-%02d-%02d %02d:%02d:%02d",
            p->tm_year, p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
    return true;
}
bool SlogInit(int _iTarget, int _iLevel, const char *_strPath) {
    g_tLog.iTarget = _iTarget;
    g_tLog.iLevel = _iLevel;
    g_tLog.iSize = 0;
    if(g_tLog.iLevel > LOG_LEVEL_MAX) {
        g_tLog.iLevel = LOG_LEVEL_MAX;
    }
    
    if(NULL == _strPath) {
        strncpy(g_tLog.strPath, LOG_DEFAULT_PATH, sizeof(g_tLog.strPath)-1);
    } else {
        strncpy(g_tLog.strPath, _strPath, sizeof(g_tLog.strPath)-1);
    }
    printf("Log path is %s\n", g_tLog.strPath);
    g_tLog.fp = fopen(g_tLog.strPath, "w+");
    if(NULL == g_tLog.fp) {
        printf("fp is null !");
        return false;
    }
    return true;
}
bool SlogDeinit() {
    if(NULL != g_tLog.fp) {
        fflush(g_tLog.fp);
        fclose(g_tLog.fp);
    }
    return true;
}
bool SlogSetLevel(int _iLevel) {
    g_tLog.iLevel = _iLevel;
    if(g_tLog.iLevel > LOG_LEVEL_MAX) {
        g_tLog.iLevel = LOG_LEVEL_MAX;
    }
    return true;
}
bool SlogGetLevel(int *_iLevel) {
    *_iLevel = g_tLog.iLevel;
    return true;
}
void Slog(int _iLevel, const char* format, ...) {
    if(_iLevel > LOG_LEVEL_MAX) {
        _iLevel = LOG_LEVEL_MAX;
    }
    if(NULL == g_tLog.fp || _iLevel >  g_tLog.iLevel) {
        return;
    }
    va_list ap;
    char strTimeBuf[32] = {0};
    struct timeval sCurTm = {0};
    gettimeofday(&sCurTm, NULL);
    TimeToString(sCurTm.tv_sec, strTimeBuf, sizeof(strTimeBuf));
    if(LOG_TARGET_TERM == g_tLog.iTarget) {
        printf("[%s.%03d][%s]", strTimeBuf, (int)sCurTm.tv_usec/1000, g_strLevel[_iLevel]);
        va_start(ap, format);
        vprintf(format, ap);
        va_end(ap);
        return;
    }
    // 大于最大重新打开
    if (g_tLog.iSize > LOG_MAX_SZIE) {
        fclose(g_tLog.fp);
        g_tLog.fp = fopen(g_tLog.strPath, "w+");
        if (NULL == g_tLog.fp) {
            printf("fp is null !");
            return;
        }
        g_tLog.iSize = 0;
    }
    g_tLog.iSize += fprintf(g_tLog.fp, "[%s.%03d][%s]", strTimeBuf, (int)sCurTm.tv_usec/1000, g_strLevel[_iLevel]);
    va_start(ap, format);
    g_tLog.iSize += vfprintf(g_tLog.fp, format, ap);
    va_end(ap);
    fflush(g_tLog.fp);
}

# main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "SimpleLog.h"
int main() {
    SlogInit(LOG_TARGET_TERM, LOG_LEVEL_DEBUG, "./test.log");
    Slog(LOG_LEVEL_ERROR, "hello world!\n");
    Slog(LOG_LEVEL_WARN, "hello world!\n");
    Slog(LOG_LEVEL_INFO, "hello world!\n");
    Slog(LOG_LEVEL_DEBUG, "hello world!\n");
    Serror("hello world!\n");
    Swarn("hello world!\n");
    Sinfo("hello world!\n");
    Sdebug("hello world!\n");
    SlogDeinit();
    
    return 0;
}