原以为一个日志模板写起来应该很简单才对,当自己真正开始动手做起来才知道没那么容易,有很多东西要考虑。事实上,写这篇博文的时候,我还没有完成我想要的日志模板,至少我认为我现在的模板不够健壮,没有达到我对它的要求。写下来,一方面是个阶段性的总结,一方面是重新审视自己的不足。
一、目标
1、日志文件命名:程序名时间.log;
2、实时记录,日志文件删除后,再调用会及时生成log日志文件;
3、日志内容包含调用的文件名、行数、时间、内容;
4、日志级别分5级:HINT、ALWAYS、DEBUG、ERROR、FATAL;
5、日志接口:GLog(int Level,char *fmt,...);
二、开发中遇到的几个问题
1、创建日志文件使用C库函数(如fopen、fwrite等)还是系统函数(如open、write等)?
这个我还不确定哪个好,暂时用的是第一种,即C库函数
2、占用资源尽量少
使用4个全局变量,分别保存工作路径、程序名、创建log文件的开关、保存日志等级的二维字符数组;
2个静态变量,分别是日志文件名和日志文件的文件指针;
3、每次调用接口,写完文件后要不要关闭文件?
如果每次调用都要开关文件的话,个人认为会很费时间,消耗资源(IO操作),所以每次调用我都没有关闭文件指针。每次写文件之前,我都会用stat函数检查文件是否存在,如果不存在就重新创建文件;我写了一个通信的程序测试。当服务端后台运行后,我尝试删除日志文件操作,删除两次就会出现程序自动退出的问题,而且没有产生core;前台运行,没有出现该问题。
附代码:
liblog.h:
#ifndef LIBLOG_H
#define LIBLOG_H
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#define BUFFSIZE 1024
#define LOGNAMESIZE 120
#define TIMESIZE 15
#define HINT 0,__FILE__,__LINE__
#define ALWAYS 1,__FILE__,__LINE__
#define DEBUG 2,__FILE__,__LINE__
#define ERROR 3,__FILE__,__LINE__
#define FATAL 4,__FILE__,__LINE__
char LogPath[100];
char ProName[20];
int iLogFlag;
char sLogLevel[5][10];
void LogInit(char *progress);
void GetLocalTime(char *localtime);
void GLog(int iLogLevel,char *file,int line,char *fmt, ...);
#endif
liblog.c:
#include "liblog.h"
/* init log module */
void LogInit(char *progress)
{
if(NULL==progress)
{
strcpy(ProName,"LOG_INIT");
}
else
{
char *pro = NULL;
int iLen = 0;
pro = strstr(progress,"./");
if(NULL==pro)
{
strcpy(ProName,progress);
}
else
{
iLen = strlen(progress);
strncpy(ProName,pro+2,iLen-2);
}
}
strcpy(LogPath,"/home/dboslnk/project/log/");
iLogFlag = 0;
strcpy(sLogLevel[0],"HINT");
strcpy(sLogLevel[1],"ALWAYS");
strcpy(sLogLevel[2],"DEBUG");
strcpy(sLogLevel[3],"ERROR");
strcpy(sLogLevel[4],"FATAL");
return ;
}
/* function : get loacal time */
void GetLocalTime(char *localtime)
{
if(NULL==localtime)
{
fprintf(stderr,"GetLocalTime parameter error\n");
return ;
}
time_t tNow = time(NULL);
struct tm pTm = {0};
char sTime[TIMESIZE];
localtime_r(&tNow,&pTm);
memset(sTime,0x00,TIMESIZE);
strftime(sTime,TIMESIZE,"%Y%m%d%H%M%S",&pTm);
/* make sure that localtime is large enough for copy */
strncpy(localtime,sTime,TIMESIZE);
}
/* function : parameter list */
void GLog(int iLogLevel,char *file,int line,char *fmt, ...)
{
va_list argstr;
static FILE *flogptr = NULL;
int iLen = 0,iRet = 0;
char buffer[BUFFSIZE];
static char LogName[LOGNAMESIZE];
char sLocalTime[TIMESIZE];
char sLogDetail[BUFFSIZE];
struct stat fBuff;
memset(buffer,0x00,BUFFSIZE);
va_start(argstr,fmt);
iLen = vsprintf(buffer,fmt,argstr);
va_end(argstr);
OPENFILE:
memset(sLocalTime,0x00,sizeof(sLocalTime));
GetLocalTime(sLocalTime);
if(iLogFlag==0)
{
/* create log file */
memset(LogName,0x00,sizeof(LogName));
sprintf(LogName,"%s%s%s.log",LogPath,ProName,sLocalTime);
flogptr = fopen(LogName,"a");
if(NULL==flogptr)
{
fprintf(stderr,"fopen log name[%s] iLogFlag[%d] error\n",LogName,iLogFlag);
}
iLogFlag = 1;
}
//sprintf(sLogDetail,"%s|%s|%d|%s|%s\n",sLocalTime,file,line,sLogLevel[iLogLevel],buffer);
//iLen = strlen(sLogDetail);
memset(&fBuff,0x00,sizeof(fBuff));
iRet = stat(LogName,&fBuff);
if(iRet<0)
{
fprintf(stderr,"stat [%s] error\n",LogName);
iLogFlag = 0;
goto OPENFILE;
}
sprintf(sLogDetail,"%s|%s|%d|%s|%s\n",sLocalTime,file,line,sLogLevel[iLogLevel],buffer);
iLen = strlen(sLogDetail);
fwrite(sLogDetail,1,iLen,flogptr);
fflush(flogptr);
return ;
}
总体上讲,我觉得要实现一个功能容易,但是要写好一段高质量的代码,真的很难。特别是性能、鲁棒性等问题,没有经验的人和有经验的人差距是很大的。我依然是个菜鸟,是一只不断努力学习的菜鸟。上面的代码,只是现在初级的代码,我还要修改和完善,可能会有很大的变化,包括里面调用的函数,我也可能换掉。读者如果有什么意见,请记得留下脚印,让我来验证你的想法