该 C 风格简易 log 日志系统,适合与Linux平台系统,主要用于格式化输出日志到本地指定的文件中,可指定log文件数目、最大大小、行数、按时间切换等功能,可满足基本的log日志功能。从项目中提炼出来,附上使用的demo,简单易懂,能快速上手。具体接口说明,参见源码。
源文件也可以到 Github 下载,地址:CEasyLocalLog。
#ifndef _LOCALLOG_H_
#define _LOCALLOG_H_
#include <stdio.h>
#include <time.h>
typedef struct {
FILE *pLogFile;
char sBaseFileName[80]; //log文件名前缀
char sLogFileName[80]; //log文件名
int iShiftType; //0 -> 大小, 1 -> 文件数, 2 -> shift by interval, 3 ->天, 4 -> 小时, 5 ->分钟
int iMaxLogNum; //log文件最大个数
int lMaxSize; //单个log文件最大大小,单位Byte
int lMaxCount; //单个log文件最大行数,单位line
int lLogCount; //当前log文件行数,单位line
time_t lLastShiftTime; //上一次切换时间,单位s
}OI_LogFile;
// @brief OI_InitLogFile: 初始化log
// @param pstLogFile: log结构指针
// @param sLogBaseName: Log文件名的前缀字符串
// @param iShiftType: log文件切换类型,
// 0->shift by size, when size > iMAX bytes
// 1->shift by LogCount, when logcount > iMAX lines
// 2->shift by interval, when elapse seconds > iMAX sec.
// 3->shift by day,
// 4->shift by hour
// 5->shift by minute
// 6->shift by day, log filename append with date.
//
// @param iMaxLogNum: 切换log文件的最大个数. 当iShiftType=6时无意义。
// @param iMAX: 如果iShiftType为0/1/2,那么表示当前log文件单位到达iMAX时进行切换,否则无意义。
// @return 成功返回0,失败 < 0
int OI_InitLogFile(OI_LogFile * pstLogFile, char *sLogBaseName, int iShiftType, int iMaxLogNum, int iMAX);
//
//@brief: Log string to logfile.
//@param: pstLogFile:log结构指针
// iLogTime:0 = do not log time, 1 = log simple time, 2 = log detail time
// sFormat:C string pointed by format to the logfile,format may include format specifiers
// ...:可变参数
//@return: <0 means failure
//@ps:__attribute__ ((format (printf, 3, 4))):提示编译器,对这个函数的调用需要像printf一样,用对应的format字符串来check可变参数的数据类型。3和4对应函数第3和第4个形参
int OI_Log(OI_LogFile * pstLogFile, int iLogTime, const char *sFormat, ...) __attribute__ ((format (printf, 3, 4)));
#endif
#include "localLog.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
//
//获取时间相关函数
//
char * OI_GetShortDateStr(const time_t * mytime) {
static char s[50];
struct tm curr = *localtime(mytime);
if(curr.tm_year > 50)
snprintf(s, sizeof(s), "%04d%02d%02d", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday);
else
snprintf(s, sizeof(s), "%04d%02d%02d", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday);
return s;
}
static char* OI_GetDateTimeStr(const time_t * mytime) {
static char s[50];
struct tm curr = *localtime(mytime);
if(curr.tm_year > 50)
snprintf(s, sizeof(s), "%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);
else
snprintf(s, sizeof(s), "%04d-%02d-%02d %02d:%02d:%02d", curr.tm_year + 2000, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec);
return s;
}
static char * OI_GetCurShortDateStr(void) {
time_t mytime = time(NULL);
return OI_GetShortDateStr(&mytime);
}
static char * OI_GetCurDateTimeStr(void) {
time_t mytime = time(NULL);
return OI_GetDateTimeStr(&mytime);
}
static int ShiftFiles(OI_LogFile * pstLogFile) {
struct stat stStat;
char sLogFileName[300];
char sNewLogFileName[300];
int i;
struct tm stLogTm, stShiftTm;
if (pstLogFile->iShiftType == 6)
return 0;
snprintf(sLogFileName,sizeof(sLogFileName),"%s.log", pstLogFile->sBaseFileName);
if(stat(sLogFileName, &stStat) < 0) {
if (errno == ENOENT) {
FILE *pfile;
if ((pfile = fopen(sLogFileName, "a+")) == NULL)
return -1;
fclose(pfile);
if (stat(sLogFileName, &stStat) < 0)
return -1;
} else {
return -1;
}
}
switch (pstLogFile->iShiftType) {
case 0:
if(stStat.st_size < pstLogFile->lMaxSize)
return 0;
break;
case 2:
if(stStat.st_mtime - pstLogFile->lLastShiftTime < pstLogFile->lMaxCount)
return 0;
break;
case 3:
if(pstLogFile->lLastShiftTime - stStat.st_mtime > 86400)
break;
memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
if(stLogTm.tm_mday == stShiftTm.tm_mday)
return 0;
break;
case 4:
if(pstLogFile->lLastShiftTime - stStat.st_mtime > 3600)
break;
memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
if(stLogTm.tm_hour == stShiftTm.tm_hour)
return 0;
break;
case 5:
if(pstLogFile->lLastShiftTime - stStat.st_mtime > 60)
break;
memcpy(&stLogTm, localtime(&stStat.st_mtime), sizeof(stLogTm));
memcpy(&stShiftTm, localtime(&pstLogFile->lLastShiftTime), sizeof(stShiftTm));
if(stLogTm.tm_min == stShiftTm.tm_min)
return 0;
break;
default:
if(pstLogFile->lLogCount < pstLogFile->lMaxCount)
return 0;
pstLogFile->lLogCount = 0;
}
for(i = pstLogFile->iMaxLogNum - 2; i >= 0; i--)
{
if(i == 0)
snprintf(sLogFileName,sizeof(sLogFileName),"%s.log", pstLogFile->sBaseFileName);
else
snprintf(sLogFileName,sizeof(sLogFileName),"%s%d.log", pstLogFile->sBaseFileName, i);
if(access(sLogFileName, F_OK) == 0)
{
snprintf(sNewLogFileName,sizeof(sNewLogFileName),"%s%d.log", pstLogFile->sBaseFileName, i + 1);
if(rename(sLogFileName, sNewLogFileName) < 0)
{
return -1;
}
}
}
time(&pstLogFile->lLastShiftTime);
return 0;
}
//@brief:初始化LOG对象
int OI_InitLogFile(OI_LogFile* pstLogFile, char *sLogBaseName, int iShiftType, int iMaxLogNum, int iMAX)
{
memset(pstLogFile, 0, sizeof(OI_LogFile));
strncat(pstLogFile->sLogFileName, sLogBaseName, sizeof(pstLogFile->sLogFileName) - 10);
strcat(pstLogFile->sLogFileName, ".log");
strncpy(pstLogFile->sBaseFileName, sLogBaseName, sizeof(pstLogFile->sBaseFileName) - 15);
pstLogFile->iShiftType = iShiftType;
pstLogFile->iMaxLogNum = iMaxLogNum;
pstLogFile->lMaxSize = iMAX;
pstLogFile->lMaxCount = iMAX;
pstLogFile->lLogCount = iMAX;
time(&pstLogFile->lLastShiftTime);
return ShiftFiles(pstLogFile);
}
int OI_Log(OI_LogFile * pstLogFile, int iLogTime,const char *sFormat, ...) {
va_list ap;
struct timeval stLogTv;
if (pstLogFile->iShiftType == 6) {
snprintf(pstLogFile->sLogFileName, sizeof(pstLogFile->sLogFileName), "%s_%s.log",
pstLogFile->sBaseFileName, OI_GetCurShortDateStr());
}
if((pstLogFile->pLogFile = fopen(pstLogFile->sLogFileName, "a+")) == NULL)
return -1;
va_start(ap, sFormat);
if(iLogTime == 1)
{
fprintf(pstLogFile->pLogFile, "[%s] ", OI_GetCurDateTimeStr());
}
else if(iLogTime == 2)
{
gettimeofday(&stLogTv, NULL);
fprintf(pstLogFile->pLogFile, "[%s.%.6d] ",
OI_GetDateTimeStr((const time_t *) &(stLogTv.tv_sec)), (int) stLogTv.tv_usec);
}
vfprintf(pstLogFile->pLogFile, sFormat, ap);
fprintf(pstLogFile->pLogFile, "\n");
va_end(ap);
pstLogFile->lLogCount++;
fclose(pstLogFile->pLogFile);
return ShiftFiles(pstLogFile);
}
/*
*@brief:C风格简易log系统使用demo
*@author:dablelv,1589276509@qq.com
*@date:20171207
*/
#include "localLog.h"
//定义三个级别LOG
OI_LogFile stErrorLog; //错误日志
OI_LogFile stDebugLog; //调试日志
OI_LogFile stEventLog; //流水日志
//宏定义调用log函数
//ps:_args_...:GCC支持的可变参数名;"##对符号进行连接,如果_args_为空,则不进行连接,使函数宏可省略可变参数
#define ErrorLog(_fmt_, _args_...) do{ OI_Log(&stErrorLog, 2, _fmt_, ##_args_); } while (0)
#define DebugLog(_fmt_, _args_...) do{ OI_Log(&stDebugLog, 2, _fmt_, ##_args_); } while (0)
#define EventLog(_fmt_, _args_...) do{ OI_Log(&stEventLog, 2, _fmt_, ##_args_); } while (0)
int main(int argc,char* argv[]) {
//初始化logFile
//保留最多10个log文件,每个最大大小约为100MB
OI_InitLogFile(&stErrorLog,"/home/dablelv/test/logSys/log/error",0,10,100000000);
OI_InitLogFile(&stDebugLog,"/home/dablelv/test/logSys/log/debug",0,10,100000000);
OI_InitLogFile(&stEventLog,"/home/dablelv/test/logSys/log/event",0,10,100000000);
//向log文件写日志
ErrorLog("this is error log");
ErrorLog("this is error log whith %s","argument");
DebugLog("this is debug log");
DebugLog("this is debug log whith %s","argument");
EventLog("this is event log");
EventLog("this is event log whith %s","argument");
}
除了上面 C 风格的 log 系统,还有一款 C++ 风格的 log 系统可供使用,参见本人的另一篇博文:C++实现简易log日志系统。
后续将总结提炼出一款远程 log 系统分享给大家,支持将日志输出到指定的远程主机。