C使用宏定义封装printf实现日志功能
问题在日常的开发中,我们经常会在代码中加一些信息便于调试和反查bug,加的这些打印对我们来说是很有帮助的,并且我们在代码发布的时候并不想删除他们,但又不想他们继续打印这些信息;思路如果需要可控的输处日志信息,最好的是我们程序有个日志系统(不是写入内核的环形缓冲区How can I write to dmesg from command line),但嵌入式设备应用一般是轻量级应用,不好带臃肿的日志
问题
在日常的开发中,我们经常会在代码中加一些信息便于调试和反查bug,加的这些打印对我们来说是很有帮助的,并且我们在代码发布的时候并不想删除他们,但又不想他们继续打印这些信息;
思路
如果需要可控的输处日志信息,最好的是我们程序有个日志系统(不是写入内核的环形缓冲区How can I write to dmesg from command line),但嵌入式设备应用一般是轻量级应用,不好带臃肿的日志库,所以最好我们能封装printf;
使用宏定义开关
#ifdef __DEBUG
#define xxxlog(info) printf(info)
#else
#define xxxlog(info)
#endif
这样子只是起到了同时开关,并不是我们想要的效果;
使用不定参数的宏定义
C99
规范(目前大部分嵌入式开发都基于该标准)后,编译器就开始支持不定参数##__VA_ARGS__
, 具体可参考怎样写参数个数可变的宏
#ifdef __DEBUG
#define xxxlog(format, ...) printf(format, ##__VA_ARGS__)
#else
#define xxxlog(format, ...)
#endif
其实我们可以根据编译的一些预定义宏,打印更多的帮助信息,例如:__FILE__
,__LINE__
,__func__
:
#ifdef __DEBUG
#define xxxlog(format, ...) \
printf("[%s:%d->%s] "format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#else
#define xxxlog(format, ...)
#endif
下例:
#include <stdio.h>
#define xxxlog(format, ...) \
printf("[%s:%d->%s] "format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
int main(int argc, char *argv[])
{
int num = 99;
xxxlog("hello, world!\n");
xxxlog("num is %d\n", num);
return 0;
}
但这还是只能全部屏蔽,我们需要最好能分级的函数,可改进如下:
#ifndef APP_PUBLIC_LOGGER_H_
#define APP_PUBLIC_LOGGER_H_
#define __DEBUG //日志模块总开关,注释掉将关闭日志输出
#ifdef __DEBUG
#define DEBUG(format, ...) printf (format, ##__VA_ARGS__)
#else
#define DEBUG(format, ...)
#endif
//定义日志级别
enum LOG_LEVEL {
LOG_LEVEL_OFF=0,
LOG_LEVEL_FATAL,
LOG_LEVEL_ERR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO
};
#define log_fatal(level,format, ...) \
do { \
if(level>=LOG_LEVEL_FATAL)\
DEBUG("\n->FATAL @ FUNC:%s FILE:%s LINE:%d \n" format "\n",\
__func__, __FILE__, __LINE__, ##__VA_ARGS__ );\
} while (0)
#define log_err(level,format, ...) \
do { \
if(level>=LOG_LEVEL_ERR)\
DEBUG("\n->ERR @ FUNC:%s FILE:%s LINE:%d \n" format "\n",\
__func__, __FILE__, __LINE__, ##__VA_ARGS__ );\
} while (0)
#define log_warn(level,format, ...) \
do { \
if(level>=LOG_LEVEL_WARN)\
DEBUG("\n->WARN @ FUNC:%s \n" format "\n",__func__, ##__VA_ARGS__ );\
} while (0)
#define log_info(level,format, ...) \
do { \
if(level>=LOG_LEVEL_INFO)\
DEBUG("\n->INFO \n"format"\n",##__VA_ARGS__ );\
} while (0)
#define log_debug(level,format, ...) \
do { \
if(level>=LOG_LEVEL_ALL)\
DEBUG("\n->DEBUG \n"format"\n",##__VA_ARGS__ );\
} while (0)
#endif /* APP_PUBLIC_LOGGER_H_ */
使用也非常简单,定义一个局部的变量来保存该模块的日志输出级别,下面是使用示例:
#include"logger.h"
static enum LOG_LEVEL logger=LOG_LEVEL_WARN;//模块日志输出级别
int main(void)
{
logger = LOG_LEVEL_ALL; //修改模块日志输出级别
log_debug(logger,"this is a debug");
log_info(logger,"this is a info");
log_warn(logger,"%s","this is a warn");
log_err(logger,"this is a err %d ",-1);
return 0;
}
还有一个思路就是将宏函数映射为真正函数,这样就可以省略掉输入的logger,在函数内部作比较。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)