RT-Thread ULOG: 创建多个文件后端并保存不同日志方法 | 技术集结

描述

目录

 

前言


 

使用场景


 

实现功能


 

具体操作

1 前言

在项目开发中需要使用到日志功能来调试和查看问题。有些问题并不会在我们实时查看的时候发生,而是在你上个厕所的功夫可能就发生了。如果上位机的缓冲区不够大,可能错误日志都看不到。

这时候就很有必要把日志文件保存在文件系统中了。RTT软件包中ULOG_FILE这个包可以实现日志文件的保存,但是它两年没更新了,另一个是会把所有日志保存起来。并不能按不同的标签分开保存。

还好现在的ulog组件自带了文件后端,只需要我们去配置就好了。

2 使用场景

在实际应用中,有些日志信息需要不间断输出并保存。例如AGV的运动信息,电池电量等等。

这些信息如果不作处理,会一并打印在控制台上,影响查看重要信息与输入MSH命令

3 实现功能

所以需要一个文件来实现:

1.日志分开保存在不同文件内。

2.控制台可以按需求显示与不显示某类信息。

4 具体操作

0.前提配置

1.ULOG组件勾选并使用异步模式。

2.使用一个文件系统来保存文件

3.ULOG组件勾选文件后端

项目开发

4.先确保文件系统能够正常读写,创建,删除

1.新建一个注册表,把目录,文件名,大小,数量写入表内

BUFF_SIZE这个缓冲区代表多少大小再进行一次写入,缓冲区太大,会很久才更改一次。

BUFF_SIZE太小会写入太频繁,导致ULOG异步线程堆栈不够溢出

ROOT_PATH是保存的路径。我选择“/flash/log”是因为我使用了ROM系统保存根目录,根目录是只读的。只有/flash下的文件夹才是可读写的。

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

struct _log_file{const char *name; const char *dir_path; rt_size_t max_num;rt_size_t max_size;rt_size_t buf_size;};#define ROOT_PATH "/flash/log"#define FILE_SIZE 512 * 1024#define BUFF_SIZE 512static struct _log_file table[] ={{"sys"      ,ROOT_PATH,10,FILE_SIZE,BUFF_SIZE},{"motion"   ,ROOT_PATH,5,FILE_SIZE,BUFF_SIZE},};

2.进行文件后端初始化

注意init后一定要enbale,不然没有运行

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  

staticstructulog_backendsys_log_backend;staticstructulog_file_besys_log_file;voidsys_log_file_backend_init(void){  structulog_file_be *file_be = &sys_log_file;  uint8_t id = sys_id;  file_be->parent = sys_log_backend;  ulog_backend_filter_t filter = sys_log_file_backend_filter;  ulog_file_backend_init( file_be,                           table[id].name,                          table[id].dir_path,                          table[id].max_num,                          table[id].max_size,                          table[id].buf_size);  ulog_file_backend_enable(file_be);}staticstructulog_backendmotion_log_backend;staticstructulog_file_bemotion_log_file;voidmotion_log_file_backend_init(void){  structulog_file_be *file_be = &motion_log_file;  uint8_t id = motion_id;  file_be->parent = motion_log_backend;  ulog_backend_filter_t filter = motion_log_file_backend_filter;  ulog_file_backend_init( file_be,                           table[id].name,                          table[id].dir_path,                          table[id].max_num,                          table[id].max_size,                          table[id].buf_size);  ulog_file_backend_enable(file_be);}

3.测试运行

这个时候就可以先测试一下,看看日志有没有都保存在两个文件内

后缀名都是.log

项目开发

使用cat命令查看一下内容

项目开发

4.分开保存不同日志

上一步只进行了同时保存在两个文件内。还需要保存不同的日志内容。

1.在初始化函数中加入滤波函数设置

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  

voidsys_log_file_backend_init(void){    ulog_backend_filter_t filter = sys_log_file_backend_filter;    ulog_backend_set_filter(&file_be->parent,filter);}

2.编写滤波函数

这个函数是自己编写的,比如系统日志文件不需要标签带有”MOVE”的
 

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

#define MOTION_TAG  "MOVE"staticrt_bool_tsys_log_file_backend_filter(struct ulog_backend *backend, rt_uint32_t level, constchar *tag, rt_bool_t is_raw,                                     constchar *log, rt_size_t len){  if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)//排除带有"MOVE"标签输出    return RT_FALSE;  else    return RT_TRUE;}

比如运动日志文件只需要标签带有”MOVE”的
 

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

staticrt_bool_tmotion_log_file_backend_filter(struct ulog_backend *backend, rt_uint32_t level, constchar *tag, rt_bool_t is_raw,                                     constchar *log, rt_size_t len){  if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)//带有"MOVE"标签输出    return RT_TRUE;  else    return RT_FALSE;}

3.在一个线程中使用带特定标签的日志输出看一下效果

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

#define LOG_MV(...)  ulog_i(MOTION_TAG, __VA_ARGS__)while(1){    set_angle += 0.1;    get_angle -= 0.1;    set_speed += 1;    get_speed -=1;    vaule += 3.14;    LOG_MV("%f %f %d %f %f",         set_angle,get_angle,set_speed,        get_speed,vaule);   rt_thread_mdelay(500);}

大小不同的保存文件
 

项目开发

一个是系统日志
 

项目开发

一个是运动日志
 

项目开发

内容也不同,搞定完成

5.实现文件后端输出功能的关闭与打开

像易掉电的嵌入式使用FLASH或者EEPROM,容易在进行文件操作时掉电,导致文件损坏。另一个就是文件打开状态下,是不能删除的。有的时候又需要清除无用的日志文件。这就需要实现可以对文件后端进行控制

1.控制日志是否输出到文件中

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  

staticvoidlog_file_backend_control(uint8_t argc, char **argv){  constchar *operator = argv[1];  constchar *flag = argv[2];  if (argc < 3)  {      rt_kprintf("Usage:\n");      rt_kprintf("control ulog file backend [name] [enable/disable]\n");      return;  }  elseif(!rt_strcmp(operator,table[sys_id].name))  {    if(!rt_strcmp(flag,"disable"))    {      ulog_file_backend_disable(&sys_log_file);      rt_kprintf("The file backend %s is disabled\n",operator);    }    elseif(!rt_strcmp(flag,"enable"))    {      ulog_file_backend_enable(&sys_log_file);      rt_kprintf("The file backend %s is enable\n",operator);    }    else    {      rt_kprintf("Usage:\n");      rt_kprintf("control ulog file backend [name] [enable/disable]\n");      return;    }  }  elseif(!rt_strcmp(operator,table[motion_id].name))  {    if(!rt_strcmp(flag,"disable"))    {      ulog_file_backend_disable(&motion_log_file);      rt_kprintf("The file backend %s is disabled\n",operator);    }    elseif(!rt_strcmp(flag,"enable"))    {      ulog_file_backend_enable(&motion_log_file);      rt_kprintf("The file backend %s is enable\n",operator);    }    else    {      rt_kprintf("Usage:\n");      rt_kprintf("control ulog file backend [name] [enable/disable]\n");      return;    }  }  else  {    rt_kprintf("Failed to find the file backend:%s\n",operator);  }}MSH_CMD_EXPORT_ALIAS(log_file_backend_control,ulog_be_ctrl,control ulog file backend [name] [enable:disable]);

2.控制日志文件后端卸载,来删除文件

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  

staticvoidlog_file_backend_deinit(uint8_t argc, char **argv){  constchar *operator = argv[1];  if (argc < 2)  {      rt_kprintf("Usage:\n");      rt_kprintf("Deinit ulog file backend [name]\n");      return;  }  else  {    if(!rt_strcmp(operator,"motion"))    {      ulog_file_backend_deinit(&motion_log_file);      ulog_file_backend_disable(&motion_log_file);      rt_kprintf("The file backend %s is deinit\n",operator);    }    elseif(!rt_strcmp(operator,"sys"))    {      ulog_file_backend_deinit(&sys_log_file);      ulog_file_backend_disable(&sys_log_file);      rt_kprintf("The file backend %s is deinit\n",operator);    }    else    {      rt_kprintf("Usage:\n");      rt_kprintf("Deinit ulog file backend [name]\n");      return;    }  }}MSH_CMD_EXPORT_ALIAS(log_file_backend_deinit,ulog_be_deinit,Deinit ulog file backend);

6.扩展功能,显示与不显示特定日志到控制台上

按照上面操作下来,基本功能都完成了。还差一个控制台可以按需求显示与不显示某类信息。

不实现这个,会导致控制台信息一直在输出,无法查看重要信息,不好输入命令

1.对console_be.c进行改造

增加ulog_console_backend_filter函数的弱定义,可以在其他地方进行实现

在控制台初始化时,挂钩ulog_console_backend_filter函数

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  


RT_WEAK rt_bool_tulog_console_backend_filter(struct ulog_backend *backend, rt_uint32_t level, constchar *tag,                                              rt_bool_t is_raw,constchar *log, rt_size_t len){  return RT_TRUE;}intulog_console_backend_init(void){    ulog_init();    console.output = ulog_console_backend_output;    ulog_backend_register(&console, "console", RT_TRUE);    ulog_backend_set_filter(&console,ulog_console_backend_filter);    return0;}INIT_PREV_EXPORT(ulog_console_backend_init);

2.实现ulog_console_backend_filter函数,过滤不需要显示信息

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

rt_bool_tulog_console_backend_filter(struct ulog_backend *backend, rt_uint32_t level, constchar *tag, rt_bool_t is_raw,                                       constchar *log, rt_size_t len){    if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)//排除带有"MOVE"标签输出      return RT_FALSE;    else      return RT_TRUE;}

3.增加MSH命令,用来控制控制台需不需要查看信息

有些时候,日志文件输出的不是实时的。打开文件查看也不方便。

能输出到控制台直接显示是最合适不过

上下左右滑动阅览

  •  
  •  
  •  
  •  
  •  

#ifdef RT_USING_MSHstaticvoidulog_console_backend_filter_set(uint8_t argc, char **argv){    if (argc < 2)    {        rt_kprintf("Usage:\n");        rt_kprintf("console filter [option] optino:enable or disable\n");        return;    }    else    {        constchar *operator = argv[1];        if (!rt_strcmp(operator, "enable"))        {            ulog_backend_set_filter(&console,ulog_console_backend_filter);        }        elseif (!rt_strcmp(operator, "disable"))        {            ulog_backend_set_filter(&console,RT_NULL);        }        else        {            rt_kprintf("Usage:\n");            rt_kprintf("console filter [option] optino:enable or disable\n");        }    }}MSH_CMD_EXPORT_ALIAS(ulog_console_backend_filter_set,console_filter,console filter [option] optino:enable or disable);#endif

7.结束

以上就是全部内容了。

这个应用根据我在github上提出的问题实现的。

https://github.com/RT-Thread/rt-thread/issues/6567

之前也有这种需求,用的是另一种方式。

https://club.rt-thread.org/ask/article/e3ace649aea0ba5c.html

稍微提一句,MSH命令的控制函数没有使用输入文件后端的名称去查找实现。因为ulog_backend_find()函数的返回只有ulog的后端指针,没有file_be的指针,没办法操作。觉得ulog_backend的结构体可以加一个子对象,就像ulog_file_be的结构体加入了parent一样

特此分享^~^

最后附上代码文件

ulog_file_be.c(https://club.rt-thread.org/file_download/d845a228140ec38b

ulog_file_be.h(https://club.rt-thread.org/file_download/ef9a1b046b884535

console_be.c(https://club.rt-thread.org/file_download/0b4e0fd7bc8a566e

【更新]

取消console_be.c的代码

MSH命令改为使用ulog_be_cmd控制

支持控制台操作

项目开发

ulog_file_be.c(https://club.rt-thread.org/file_download/db82838a83e98553

 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分