日常工作中常常需要输出日志进行调试,本文提供了一个 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; | |
} |