前面的描述可能不够好,毕竟文字水平有限,有些东西太难用文字表达,大家可以直接看代码,代码里有着注释
宏定义控制级别的实现
这个日志系统我是通过宏定义来进行分级别打印的,为什么选择在编译前实现分级别,而不是在编译后用函数接口来实现分级别打印的。两者各有各的优点,前者能够带来比较好的效率,级别的比较都是通过预处理来执行的,这样在运行过程中会省下一部分时间,然而如果使用第二种是不是一但需要的打印的东西多了,程序就要不断的调用函数,这样显然浪费很多时间,但是这样比较便捷灵活,各有各的好处。
首先我们要了解宏定义以及##粘合剂和可变变参宏__VA_ARGS__,相信大家的对于宏定义的基本用法大概都知道,这里我们将了解下##粘合剂和变参宏。
##粘合剂
##运算符可用于类函数宏的替换部分,而且,##还可用于对象宏的替换部分, ##运算符把两个记号组合成一个记号。例如下面伪代码
#define NAME(n) x ## n
int NAME(1) = 14; 变成 int x1 = 14;
int NAME(2) = 14; 变成 int x2 = 14;
简单来所就是把x和n都来个的值合在一起,
变参宏…和__VA+ARGS__
这种灵活的定义宏的方式,给了我们极大的便利, 能够让我们不用限制宏函数的参数的类型和个数,编译器会自动适配,如下面
#define PR(...) printf(__VA_ARGS__)
PR("howday");
PR("weight = %d, shipping = $%.2f\n", wt, sp);
对于第一次调用,__VA_ARGS__展开为1个参数:“howday"
对于第一次调用,__VA_ARGS__展开为2个参数:“weight = %d, shipping = $%.2f\n”、 wt、 sp
所以,展开的代码为
printf("howday");
printf("weight = %d, shipping = $%.2f\n", wt, sp);
运用上面两个技巧我们可以完成下面分级别部分的宏定义
宏定义级别输入代码
/*-----------------------------------------------------------------------------
* 打印输出级别
*-----------------------------------------------------------------------------*/
#define DEBUG 5
#define ENTRY 4
#define INFO 3
#define WARN 2
#define ERROR 1
#define NONE 0
/*-----------------------------------------------------------------------------
* 如果在编译阶段不定义PRINT_LEVEL,则默认DEBUG模式打印,如果用-D PRINT_LEVEL=?
* 则在某个级别模式下打印,如下列:gcc log.c -D PRINT_LEVEL=INFO(或者是对应得数字)
* 则在INFO模式下打印。
*-----------------------------------------------------------------------------*/
#ifndef PRINT__LEVEL
#define DEBUG_LEVEL INFO //默认级别
#else
#define DEBUG_LEVEL PRINT__LEVEL
#endif
#define LOG_DEBUG(args,...)\
do{\
if (DEBUG_LEVEL >= DEBUG)\
{\
printf("[D] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_ENTRY(args,...)\
do{\
if (DEBUG_LEVEL >= ENTRY)\
{\
printf("[L] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_INFO(args,...)\
do{\
if (DEBUG_LEVEL >= INFO)\
{\
printf("[I] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_WARN(args,...)\
do{\
if (DEBUG_LEVEL >= WARN)\
{\
printf("[W] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_ERROR(args,...)\
do{\
if (DEBUG_LEVEL >= ERROR)\
{\
printf("[E] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
#define LOG_NONE(args,...)\
do{\
if (DEBUG_LEVEL >= NONE)\
{\
printf("[N] (%s, %d) "args"", __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}while(0)
这样我们就完成了级别的控制,首先我们定义了六种打印级别,安大到小一一排列,根据大小比较来进行打印的控制,下面代码的这一部分是为了设置默认级别的,如果不进行修改就按默认级别及其比它小的级别打印输出,我们可以在编译阶段通过gcc -D 选项中改变默认级别,具体可参考注释
#ifndef PRINT__LEVEL
#define DEBUG_LEVEL INFO //默认级别
#else
#define DEBUG_LEVEL PRINT__LEVEL
#endif
这样就完成了级别的控制。
选择输出的地方
标准打印输出
这个没什么好说的,
日志文件的输出
日志文件的输出,我们首先要判断文件存不存在,用access函数可以实现这一功能,如果不存在我们就要创建这个文件,直接掉要open函数或者creat函数创建函数是不可取,因为创建的文件的文件屏蔽掩码一般默认为002,也就是会组织其它用户的写入文件,可在命令行中查看所以我们可以在创建文件前使用umask函数先设置好屏蔽为,具体的细节可以查看UNIX环境高级编程这本书,
调用open函数时记得一定又有O_APPEND这个选项,不然每次写都不会追加在后面的,
如果我们想知道上次进入和上次进入并且修改了文件的时间,我们可以使用fstat这个函数,它记录了文件的一些信息,然后就可以打印时间信息</