Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ulog
文件系统
ULOG 创建多个文件后端并保存不同日志方法
发布于 2022-10-28 16:38:08 浏览:4340
订阅该版
[tocm] # ULOG 创建多个文件后端并保存不同日志方法 ## 一、前言 - 在项目开发中需要使用到日志功能来调试和查看问题。有些问题并不会在我们实时查看的时候发生,而是在你上个厕所的功夫可能就发生了。如果上位机的缓冲区不够大,可能错误日志都看不到。 - 这时候就很有必要把日志文件保存在文件系统中了。RTT软件包中ULOG_FILE这个包可以实现日志文件的保存,但是它两年没更新了,另一个是会把所有日志保存起来。并不能按不同的标签分开保存。 - 还好现在的ulog组件自带了文件后端,只需要我们去配置就好了。 ## 二、使用场景 - 在实际应用中,有些日志信息需要不间断输出并保存。例如AGV的运动信息,电池电量等等。 - 这些信息如果不作处理,会一并打印在控制台上,影响查看重要信息与输入MSH命令 ## 三、实现功能 - 所以需要一个文件来实现: 1.日志分开保存在不同文件内。 2.控制台可以按需求显示与不显示某类信息。 ## 具体操作 ### 0.前提配置 - 1.ULOG组件勾选并使用异步模式。 - 2.使用一个文件系统来保存文件 - 3.ULOG组件勾选文件后端 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/3c4090b3b917027fefa40612245be7a5.png) - 4.先确保文件系统能够正常读写,创建,删除 ### 1.新建一个注册表,把目录,文件名,大小,数量写入表内 - BUFF_SIZE这个缓冲区代表多少大小再进行一次写入,缓冲区太大,会很久才更改一次。 - BUFF_SIZE太小会写入太频繁,导致ULOG异步线程堆栈不够溢出 - ROOT_PATH是保存的路径。我选择“/flash/log”是因为我使用了ROM系统保存根目录,根目录是只读的。只有/flash下的文件夹才是可读写的。 ```c // 后端注册表 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 512 static struct _log_file table[] = { {"sys" ,ROOT_PATH,10,FILE_SIZE,BUFF_SIZE}, {"motion" ,ROOT_PATH,5,FILE_SIZE,BUFF_SIZE}, }; ``` ### 2.进行文件后端初始化 - 注意init后一定要enbale,不然没有运行 ```c static struct ulog_backend sys_log_backend; static struct ulog_file_be sys_log_file; void sys_log_file_backend_init(void) { struct ulog_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); } static struct ulog_backend motion_log_backend; static struct ulog_file_be motion_log_file; void motion_log_file_backend_init(void) { struct ulog_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 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/822ac621b190f09cac25b0c49a6f7dfc.png) - 使用cat命令查看一下内容 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/15e2f4ef7372feea5b47018c05353d1a.png) ### 4.分开保存不同日志 - 上一步只进行了同时保存在两个文件内。还需要保存不同的日志内容。 - 1.在初始化函数中加入滤波函数设置 ```c void sys_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"的 ```c #define MOTION_TAG "MOVE" static rt_bool_t sys_log_file_backend_filter(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len) { if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)//排除带有"MOVE"标签输出 return RT_FALSE; else return RT_TRUE; } ``` - 比如运动日志文件只需要标签带有"MOVE"的 ```c static rt_bool_t motion_log_file_backend_filter(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len) { if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)//带有"MOVE"标签输出 return RT_TRUE; else return RT_FALSE; } ``` - 3.在一个线程中使用带特定标签的日志输出看一下效果 ```c #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); } ``` - 大小不同的保存文件 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/5e82a40390be5cb01b4ec547ed9715a2.png) 一个是系统日志 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/8edfe499ba13ea541fec682053339c72.png) 一个是运动日志 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/3b9a5377b37275cc331d3872e678ee62.png) - 内容也不同,搞定完成 ### 5.实现文件后端输出功能的关闭与打开 - 像易掉电的嵌入式使用FLASH或者EEPROM,容易在进行文件操作时掉电,导致文件损坏。另一个就是文件打开状态下,是不能删除的。有的时候又需要清除无用的日志文件。这就需要实现可以对文件后端进行控制 - 1.控制日志是否输出到文件中 ```c static void log_file_backend_control(uint8_t argc, char **argv) { const char *operator = argv[1]; const char *flag = argv[2]; if (argc < 3) { rt_kprintf("Usage:\n"); rt_kprintf("control ulog file backend [name] [enable/disable]\n"); return; } else if(!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); } else if(!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; } } else if(!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); } else if(!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.控制日志文件后端卸载,来删除文件 ```c static void log_file_backend_deinit(uint8_t argc, char **argv) { const char *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); } else if(!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函数 ```c RT_WEAK rt_bool_t ulog_console_backend_filter(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,const char *log, rt_size_t len) { return RT_TRUE; } int ulog_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); return 0; } INIT_PREV_EXPORT(ulog_console_backend_init); ``` - 2.实现ulog_console_backend_filter函数,过滤不需要显示信息 ```c rt_bool_t ulog_console_backend_filter(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *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命令,用来控制控制台需不需要查看信息 - 有些时候,日志文件输出的不是实时的。打开文件查看也不方便。 - 能输出到控制台直接显示是最合适不过 ```c #ifdef RT_USING_MSH static void ulog_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 { const char *operator = argv[1]; if (!rt_strcmp(operator, "enable")) { ulog_backend_set_filter(&console,ulog_console_backend_filter); } else if (!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一样 - 另外API手册多久更新一次啊。ulog_backend_set_filter就没有找到说明。 > https://www.rt-thread.org/document/api/group__ulog.html - 用API手搜索功能,函数名称贴上去就没有有出来过,比如搜索open ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221028/6dd3ccb4fb6c8300a6454fc99430342e.png.webp) - 特此分享^~^ - 最后附上代码文件 [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) ###[更新2022.11.10] - 取消console_be.c的代码 - MSH命令改为使用ulog_be_cmd控制 - 支持控制台操作 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20221110/31ca4c9398cca35bdae50a9e754bba1c.png) [ulog_file_be.c](https://club.rt-thread.org/file_download/db82838a83e98553)
21
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
用户名由3_15位
这家伙很懒,什么也没写!
文章
5
回答
242
被采纳
34
关注TA
发私信
相关文章
1
【文件系统】目录查询
2
文件系统Posix 接口 的close API疑问
3
dfs_mount挂载文件系统路径的路径必须为‘/’才能成功
4
SD卡连续读写文件报错
5
文件系统挂载断言机制
6
文件系统是否支持挂载NFS网络文件系统
7
文件系统挂载失败!!!
8
dfs_filesystem_lookup() 返回NULL
9
webnet 是否可以做全动态网页,使用内存池来加快速度
10
“文件系统装在表”报错
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
5
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部