一、EasyLogger介绍
EasyLogger 是一款超轻量级(ROM<1.6K, RAM<0.3K)、高性能的 C/C++ 日志库,非常适合对资源敏感的软件项目,例如: IoT 产品、可穿戴设备、智能家居等等。相比 log4c、zlog 这些知名的 C/C++ 日志库, EasyLogger 的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。
主要特性:
1.支持用户自定义输出方式(例如:终端、文件、数据库、串口、485、Flash…);
2.日志内容可包含级别、时间戳、线程信息、进程信息等;
3.日志输出被设计为线程安全的方式,并支持 异步输出 及 缓冲输出 模式;
4.支持多种操作系统(RT-Thread、UCOS、Linux、Windows…),也支持裸机平台;
5.日志支持 RAW格式 ;
6.支持按 标签 、 级别 、 关键词 进行动态过滤;
7.各级别日志支持不同颜色显示
8.扩展性强,支持以插件形式扩展新功能。
girhub开源地址:https://github.com/armink/EasyLogger
二、移植
elog.c--------------------elog核心功能源码
elog_port.c------------elog移植接口文件
elog_utils.c-----------elog所用到的一些c库工具函数
elog_buf.c--------------(可选)elog缓冲输出模式源码
elog_async.c---------(可选)elog异步输出模式源码
代码部分
elog_init();//初始化elog 初始化的 EasyLogger 的核心功能,初始化后才可以使用下面的API。
//日志颜色功能是将各个级别日志按照颜色进行区分,默认颜色功能是关闭的
//true: 使能,false: 失能
elog_set_text_color_enabled(true);
/* 设置每个级别的日志输出格式*/
//输出所有内容
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
//输出日志级别信息和日志TAG
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG);
elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG);
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG);
//除了时间、进程信息、线程信息之外,其余全部输出
elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO));
//输出所有内容
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL);
//启动elog 在初始化完成后,必须调用启动方法,日志才会被输出。
elog_start();
log_a("Hello EasyLogger!");
log_e("Hello EasyLogger!");
log_w("Hello EasyLogger!");
log_i("Hello EasyLogger!");
log_d("Hello EasyLogger!");
log_v("Hello EasyLogger!");
所有日志的级别关系大小如下:
级别 标识 描述
0 [A] 断言(Assert)
1 [E] 错误(Error)
2 [W] 警告(Warn)
3 [I] 信息(Info)
4 [D] 调试(Debug)
5 [V] 详细(Verbose)
串口输出打印效果:
三、详细使用
所有级别的日志输出方法如下,每种级别都有两种简化方式,用户可以自行选择。
#define elog_assert(tag, ...)
#define elog_a(tag, ...) //简化方式1,每次需填写 LOG_TAG
#define log_a(...) //简化方式2,LOG_TAG 已经在文件顶部定义,使用前无需填写 LOG_TAG
#define elog_error(tag, ...)
#define elog_e(tag, ...)
#define log_e(...)
#define elog_warn(tag, ...)
#define elog_w(tag, ...)
#define log_w(...)
#define elog_info(tag, ...)
#define elog_i(tag, ...)
#define log_i(...)
#define elog_debug(tag, ...)
#define elog_d(tag, ...)
#define log_d(...)
#define elog_verbose(tag, ...)
#define elog_v(tag, ...)
#define log_v(...)
输出标签设置
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL & ~ELOG_FMT_P_INFO);
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
日志库的默认串口为串口1的printf
需要更改调试日志的输出串口时,只需要修改【重定向printf端口】映射就行
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
在打印的日志信息中加上时间戳信息
在elog_port.c文件中找到elog_port_get_time(void)函数,在这个函数中添加系统的时间信息即可
const char *elog_port_get_time(void)
{
/* add your code here */
// return "";
return time_Data;
}
在这里我返回了一个全局的外部变量时间数组,这个数组在主函数中会时时更新系统时间戳时间
运行效果:
将打印到串口的日志信息保存到SD卡文件中
需要修改Easylogger日志输出驱动文件,使调用log_x("日志")是能输出日志到sd卡,并按照日期和时间进行存储。
在elog_port.c文件中找到elog_port_output()函数【日志最终输出的末端接口。】
/**
* output log port interface
*日志最终输出的末端接口。可以在里面增加输出到终端、输出到文件、输出到Flash等方法。
* @param log output of log
* @param size log size
*/
void elog_port_output(const char *log, size_t size)
{
if(isActive){
#if ELOG_FILE_ENABLE
elog_file_write(log, size);
#endif
}
}
e w i
在elog_file.c中
ElogErrCode elog_file_init(void)
{
ElogErrCode result = ELOG_NO_ERR;
ElogFileCfg cfg;
int year=0,month=0,date=0;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
year = RTC_DateStruct.RTC_Year;
month = RTC_DateStruct.RTC_Month;
date = RTC_DateStruct.RTC_Date;
FRESULT f_res;
/* 得到当前日期 */
sprintf(dir_path,"0:%d_%d_%d",year,month,date);
if (init_ok)
goto __exit;
elog_file_port_init();
/*新建对应文件夹*/
f_res = f_mkdir(dir_path);
if(f_res != FR_OK)
{
}
// cfg.name = ELOG_FILE_NAME;
cfg.max_size = ELOG_FILE_MAX_SIZE;
cfg.max_rotate = ELOG_FILE_MAX_ROTATE;
elog_get_new_name(logname,100);
cfg.name = logname;
elog_file_config(&cfg);
init_ok = true;
__exit:
return result;
}
int year=0,month=0,date=0;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
year = RTC_DateStruct.RTC_Year;
month = RTC_DateStruct.RTC_Month;
date = RTC_DateStruct.RTC_Date;
FRESULT f_res;
/* 得到当前日期 */
sprintf(dir_path,"0:%d_%d_%d",year,month,date);
创建了日期的文件夹
而后在elog_file.c中的elog_get_new_name()函数中创建文件
uint8_t elog_get_new_name(char *name,uint8_t len)
{
uint16_t i;
char path[256];
char time_str[20];
FIL tmp_fp;
for (i = 0; i < ELOG_FILE_MAX_ROTATE; i++) {
snprintf(path, len, "%s/%s_%d", dir_path,time_Data,i);
if(f_open(&tmp_fp, path, FA_READ) == FR_OK){
/*能打开说明已经存在*/
f_close(&tmp_fp);
}else{
strcpy(name,path);
return 0;
}
}
return -1;
}