目录
首先,我们需要定义一些枚举、宏、常量
日志的级别
typedef enum {
LOG_LEV_DEBUG, //调试
LOG_LEV_INFO, //信息提示
LOG_LEV_NOTIFICATION, //通知
LOG_LEV_WARNING, //警告
LOG_LEV_ERROR, //错误
LOG_LEV_CRITICAL, //严重
LOG_LEV_ALERT, //报警
LOG_LEV_EMERGENCY, //紧急
LOG_LEV_BUTT
}SYS_LOG_LEVEL;
枚举,定义日志级别。
另外,为了方便,我们可以自己定义一些类型
如:
typedef char uchar_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef unsigned long ulong_t;
typedef unsigned int bool_t;
#ifndef null
#define null 0
#endif
#ifndef null_ptr
#define null_ptr ((void*)0)
#endif
#ifndef null_byte
#define null_byte (uint8_t)0xFF
#endif
#ifndef null_byte_word
#define null_byte_word (uint16_t)0xFFFF
#endif
#ifndef null_byte_dword
#define null_byte_dword (uint32_t)0xFFFFFFFF
#endif
其次,可以定义一些宏
#define MAX_PATH_LEN 512 //日志文件路径长度
#define MAX_FILE_NAME_LEN 64 //日志文件名长度
#define MAX_FULL_PATH_LEN (MAX_PATH_LEN+MAX_FILE_NAME_LEN)
#define MAX_SZBUF_LEN 512 //日志文件单次打印缓存大小
#define MAX_LOG_TM_STR_LEN 32 //时间字符串长度
还需要定义一些东西,打印的时候需要:
uchar_t* g_ucLogLevel[LOG_LEV_BUTT] = {
"LEVEL_DEBUG",
"LEVEL_INFO",
"LEVEL_NOTIFICATION",
"LEVEL_WARNING",
"LEVEL_ERROR",
"LEVEL_CRITICAL",
"LEVEL_ALERT",
"LEVEL_EMERGENCY"
};
需要定义个文件句柄,因为系统运行过程中是要保持文件句柄不丢失的,所以要定义一个全局变量去保存。
FILE* g_fpLogFile = (FILE*)null_ptr;
还有日志文件名:
uchar_t g_aclogFullName[MAX_FULL_PATH_LEN] = "./debug_log.log";
记录打印时间
写个函数,通过调用系统函数,获取时间信息。这个可选吧,一般要写日志,都是会打印的,具体的格式可以自由选择的。
void LogServerCurrentTime(uchar_t *pcTimeStr)
{
struct timeval d_stTv;
struct tm * d_stTm;
time_t t;
(void)gettimeofday (&d_stTv, NULL);
time(&t);
d_stTm = localtime(&t);
(void)sprintf(pcTimeStr, "%02d_%02d_%02d-%02d:%02d:%02d.%06lu ",
(1900 + d_stTm->tm_year),
(1 + d_stTm->tm_mon),
d_stTm->tm_mday,
d_stTm->tm_hour,
d_stTm->tm_min,
d_stTm->tm_sec,
d_stTv.tv_usec);
return;
}
写日志文件
很简单的操作,就是打开文件,然后读取参数,然后写入日志文件。
我这里用例文件句柄存储,每次只要不是空,说明文件被打开着,就不需要再打开。当然如果你不嫌麻烦的话,可以每次都是打开一下,在写完信息之后fclose。
void LogServerRecordLog(const uchar_t* pcFile, const uchar_t* pcFunc,uint32_t uiLineNo, uint32_t uiLogLev, const uchar_t* pcLogFmt, ...)
{
va_list ArgList;
uchar_t acLogTime[MAX_LOG_TM_STR_LEN + 1] = {0};
uchar_t szBuffer[MAX_SZBUF_LEN + 1] = "";
uchar_t *pcLogBuff=null_ptr;
if (LOG_LEV_BUTT <= uiLogLev)
{
return;
}
if((FILE *)null_ptr == g_fpLogFile)
{
g_fpLogFile = fopen(g_aclogFullName, "a+");
if((FILE *)null_ptr == g_fpLogFile)
{
return;
}
}
va_start(ArgList, pcLogFmt);
vsprintf_s(szBuffer, MAX_SZBUF_LEN, pcLogFmt, ArgList);
va_end(ArgList);
LogServerCurrentTime(acLogTime);
fprintf(g_fpLogFile, "[ %s- %s %s %s:%d ] %s\n",
acLogTime,g_ucLogLevel[uiLogLev], pcFile, pcFunc, uiLineNo, szBuffer);
fflush(g_fpLogFile);
(void)va_end(ArgList);
return;
}
写日志的宏
上面只是实现的函数,我们平时写日志不可能传入这么多参数,最多的都是传入日志级别和要打印的内容,那么就需要定义一个宏去做这些事情了,其他的如line,func,file这些都是可以用系统的宏去获取的。
void LogServerRecordLog(const uchar_t* pcFile, const uchar_t* pcFunc,uint32_t uiLineNo, uint32_t uiLogLev, const uchar_t* pcLogFmt, ...);
#define LOG_RECORD(logLev, fmt, ...) \
LogServerRecordLog(__FILE__, __FUNCTION__,(uint32_t)__LINE__, (uint32_t)logLev, fmt, ##__VA_ARGS__)
扩展
系统可以自己去扩展,比如:
1、增加日志文件名指定的函数接口
2、增加日志级别指定的接口
3、可以增加其他的日志记录类型,如:不打印日期、不打印文件名等
4、自定义日志文件大小,超过之后是删除还是重命名等