问题

在日常的开发中,我们经常会在代码中加一些信息便于调试和反查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;
 }

demo

但这还是只能全部屏蔽,我们需要最好能分级的函数,可改进如下:

#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,在函数内部作比较。

  1. c语言宏定义##__VA_ARGS__封装printf函数
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐