在嵌入式开发中,开发阶段打印日志是追踪BUG常用手段,但是一旦到发布阶段,用来打印的日志一般需要不体现在发布中----即不被用户看到,同时在发布的版本出现问题时,还能使用原来的log定位问题源,那么使用一般的print语句,就不合适了,现在给大家介绍一下好用的log用法和框架。
源码在此
一、宏定义log实现
先给大家简单介绍一下常用条件编译相关的预编译指令
#define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用,判断某个宏是否被定义
那么使用宏定义头+ifdef语句就可一键开关所有log日志打印
#include <stdio.h>
#define LOGS1 1 //定义宏常量-是
#define LOGS 0 //定义宏常量-否
void main(){
#ifdef LOGS1
printf("log print1\n");
#endif
#ifdef LOGS
printf("log print2\n");
#endif
#ifdef LOGS1
printf(“log print3\n");
#endif
}
二、函数时宏定义log
将log处理过程变为函数,然后以宏定义全局控制
函数介绍:
vsnprintf()作用:
使用vsnprintf()用于向一个字符串缓冲区打印格式化字符串,且可以限定打印的格式化字符串的最大长度。此函数需要C99或者C++11及以上版本才能支持。
int vsnprintf (char * sbuf, size_t n, const char * format, va_list arg);
参数sbuf:用于缓存格式化字符串结果的字符数组
参数n:限定最多打印到缓冲区sbuf的字符的个数为n-1个,因为vsnprintf还要在结果的末尾追加\0。如果格式化字符串长度大于n-1,则多出的部分被丢弃。如果格式化字符串长度小于等于n-1,则可以格式化的字符串完整打印到缓冲区sbuf。一般这里传递的值就是sbuf缓冲区的长度。
参数format:格式化限定字符串
参数arg:可变长度参数列表
返回:成功打印到sbuf中的字符的个数,不包括末尾追加的\0。如果格式化解析失败,则返回负数。
#include <stdio.h>
#include <stdarg.h>
#define OPEN_LOGS2 1
#define LOG_LEVEL LOG_DEBUG
typedef enum
{
LOG_DEBUG = 0,
LOG_INFO,
LOG_WARN,
LOG_ERROR,
} E_LOGLEVEL;
char *ME_LOGLevelGet(const int level)
{
if (level == LOG_DEBUG)
{
return "DEBUG";
}
else if (level == LOG_INFO)
{
return "INFO";
}
else if (level == LOG_WARN)
{
return "LOG_WARN";
}
else if (level == LOG_ERROR)
{
return "LOG_ERROR";
}
return "UNKNON";
}
void ME_LOG(const int level, const char* fun,const int line,const char *fmt, ...)
{
#ifdef OPEN_LOGS2
va_list arg; // 宏定义参数
va_start(arg, fmt); // 获取可变参数列表的第一个参数的地址(arg是类型为va_list的指针,fmt是可变参数最左边的参数)
char buf[1 + vsnprintf(NULL, 0, fmt, arg)];
vsnprintf(buf, sizeof(buf), fmt, arg);
va_end(arg); // 清空va_list可变参数列表
if (level >= LOG_LEVEL)
printf("[%s][%s %d]%s\n", ME_LOGLevelGet(level),fun,line,buf);
#endif
}
#define EMLog(level,fmt...) ME_LOG(level,__FUNCTION__,__LINE__,fmt)
//在这里定义是因为函数ME_LOG的定义范围,__FUNCTION__,__LINE__是内部参数定义,能够显示代码所在函数与行数
void main()
{
main1();
int a = 10, b = 11;
EMLog(LOG_DEBUG, "app start");
EMLog(LOG_INFO, "A= %d", a);
EMLog(LOG_WARN, "app LOG_WARN");
EMLog(LOG_ERROR, "app LOG_ERROR");
}