欢迎光临
我们一直在努力

RT-Thread Ulog 组件使用详解:创建多个文件后端保存日志的方法

这里写目录标题

  • 一、RT-Thread Ulog 组件使用详解
    • 1、 引言
    • 2、 Ulog 组件概述
    • 3、 启用与配置 Ulog
      • 3.1、 在 Env 工具中启用
      • 3.2、 手动配置(Kconfig 或 SConscript)
    • 4、核心 API 与基础使用
      • 4.1、 日志级别
      • 4.2 、基础日志宏
      • 4.3、 带标签的日志
    • 5、 高级功能详解
      • 5.1 、异步输出模式
      • 5.2 、日志过滤
    • 6、总结
    • 7、使用示例
  • 二、创建多个文件后端并保存不同日志方法
    • 1、首先保证文件系统能正常读写
    • 2、在配置中打开文件后端
    • 3、创建一个文件信息表 将文件信息填写完成
    • 4、文件后端初始化
    • 5、运行查看
    • 6、实现命令控制后端是否输出

一、RT-Thread Ulog 组件使用详解

1、 引言

在嵌入式开发中,日志系统是调试、追踪和监控程序运行状态不可或缺的工具。RT-Thread 作为一款优秀的实时操作系统,提供了功能强大且灵活的日志组件——Ulog(Ultra-Lightweight Log)。本文将深入解析 Ulog 组件的架构、配置方法、使用技巧以及实战应用,帮助开发者快速掌握这一核心调试利器。 在这里插入图片描述

2、 Ulog 组件概述

Ulog 是 RT-Thread 内置的超轻量级日志系统,具有以下核心特点:

  • 超轻量级:核心代码精简,对资源占用极小,非常适合资源受限的嵌入式环境。
  • 异步日志:支持前端(日志输出)与后端(日志输出方式)解耦,前端执行时间恒定,不影响实时性。
  • 多种后端支持:支持控制台(串口)、文件系统、网络等多种日志输出方式。
  • 高可配置性:日志级别、格式、过滤、标签等均可灵活配置。
  • 线程安全与中断安全:可在多线程及中断上下文中安全调用。

3、 启用与配置 Ulog

3.1、 在 Env 工具中启用

在这里插入图片描述

使用 RT-Thread Env 工具或 menuconfig 配置系统是启用 Ulog 最便捷的方式:

# 进入项目 BSP 目录
scons –menuconfig

在配置界面中,依次进入:

RT-Thread Components → Utilities → Enable ulog(启用 ulog)

ulog 配置选项说明如下所示,一般情况下使用默认配置即可:

[*] Enable ulog /* 使能 ulog */
The static output log level./* 选择静态的日志输出级别。选择完成后,比设定级别低的日志(这里特指使用 LOG_X API 的日志)将不会被编译到 ROM 中 */
[ ] Enable ISR log. /* 使能中断 ISR 日志,即在 ISR 中也可以使用日志输出 API */
[*] Enable assert check. /* 使能断言检查。关闭后,断言的日志将不会被编译到 ROM 中 */
(128) The log's max width. /* 日志的最大长度。由于 ulog 的日志 API 按行作为单位,所以这个长度也代表一行日志的最大长度 */
[ ] Enable async output mode. /* 使能异步日志输出模式。开启这个模式后,日志不会立刻输出到后端,而是先缓存起来,然后交给日志输出线程(例如:idle 线程)去输出 */
log format -> /* 配置日志的格式,例如:时间信息,颜色信息,线程信息,是否支持浮点等等 */
[*] Enable console backend. /* 使能控制台作为后端。使能后日志可以输出到控制台串口上。建议保持开启。 */
[ ] Enable file backend. /* ulog 的文件后端 */
[ ] Enable runtime log filter. /* 使能运行时的日志过滤器,即动态过滤。使能后,日志将支持按标签、关键词等方式,在系统运行时进行动态过滤。 */
[ ] Enable syslog format log and API. /* 启用 syslog 格式日志和 API */

配置日志的格式(log format)选项描述如下所示:

[ ] Enable float number support. It will using more thread stack. /* 浮点型数字的支持(传统的 rtdbg/rt_kprintf 均不支持浮点数日志) */
[*] Enable color log. /* 带颜色的日志 */
[*] Enable time information. /* 时间信息 */
[ ] Enable timestamp format for time. /* 包括时间戳 */
[*] Enable level information. /* 级别信息 */
[*] Enable tag information. /* 标签信息 */
[ ] Enable thread information. /* 线程信息 */

3.2、 手动配置(Kconfig 或 SConscript)

对于更高级的定制,可以直接修改 rtconfig.h 或项目的 Kconfig 文件:

// rtconfig.h 示例配置
#define ULOG_USING_COLOR
#define ULOG_OUTPUT_LVL LOG_LVL_DBG
#define ULOG_ASYNC_OUTPUT_ENABLE
#define ULOG_ASYNC_OUTPUT_BUF_SIZE 1024

4、核心 API 与基础使用

4.1、 日志级别

Ulog 定义了 5 个日志级别,从低到高依次为:

级别名称描述
LOG_LVL_ASSERT 断言 发生无法处理、致命性的的错误,以至于系统无法继续运行的断言日志
LOG_LVL_ERROR 错误 发生严重的、不可修复的错误时输出的日志属于错误级别日志
LOG_LVL_WARNING 警告 出现一些不太重要的、具有可修复性的错误时,会输出这些警告日志
LOG_LVL_INFO 信息 给本模块上层使用人员查看的重要提示信息日志,例如:初始化成功,当前工作状态等。该级别日志一般在量产时依旧保留
LOG_LVL_DBG 调试 给本模块开发人员查看的调试日志,该级别日志一般在量产时关闭

4.2 、基础日志宏

最常用的日志输出宏如下:

#include <ulog.h>

// 基础格式输出
LOG_D("This is a debug message: %d", value); // 调试
LOG_I("System started successfully."); // 信息
LOG_W("Memory is running low: %d%% free", free_percent); // 警告
LOG_E("Failed to open file: %s", filename); // 错误

// 断言(条件为假时触发)
LOG_ASSERT(ptr != NULL); // 如果 ptr 为 NULL,会输出断言信息并可能触发断言处理

4.3、 带标签的日志

为不同模块或组件定义标签,便于过滤和管理:

// 定义标签(通常在c文件中)
#define DBG_TAG "command"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

// 使用标签输出
LOG_D("Socket connected, fd=%d", sock_fd);
LOG_I("DHCP request sent");
LOG_E("Connection timeout");

5、 高级功能详解

5.1 、异步输出模式

异步模式将日志放入缓冲区,由独立的后端线程输出,极大减少对调用线程的阻塞。

// 启用异步模式后,日志调用立即返回
LOG_I("This log is put into buffer and will be output by backend thread.");

// 可以设置缓冲区大小,防止溢出
// 在 menuconfig 中配置 ULOG_ASYNC_OUTPUT_BUF_SIZE

5.2 、日志过滤

Ulog 支持基于级别和标签的精细过滤:

// 为特定标签设置级别
ulog_tag_lvl_filter_set("NETWORK", LOG_LVL_DBG); // NETWORK 标签可输出 DEBUG
ulog_tag_lvl_filter_set("FS", LOG_LVL_WARNING); // FS 标签只输出 WARNING 及以上

6、总结

RT-Thread Ulog 组件是一个设计精良、功能全面的嵌入式日志解决方案。通过本文的介绍,您应该已经掌握了:

  • Ulog 的启用与基础配置方法。
  • 各级别日志宏的使用场景。
  • 利用标签进行模块化日志管理。
  • 异步输出、过滤等高级功能的配置。
  • 如何扩展后端,将日志输出到文件或网络。
  • 在实际项目中集成和优化 Ulog 的最佳实践。
  • 7、使用示例

    #define LOG_TAG "example"
    #define LOG_LVL LOG_LVL_DBG
    #include <ulog.h>

    LOG_D("LOG_D(%d): RT-Thread is an open source IoT operating system from China.", count);
    LOG_I("LOG_I(%d): RT-Thread is an open source IoT operating system from China.", count);
    LOG_W("LOG_W(%d): RT-Thread is an open source IoT operating system from China.", count);
    LOG_E("LOG_E(%d): RT-Thread is an open source IoT operating system from China.", count);

    在这里插入图片描述

    二、创建多个文件后端并保存不同日志方法

    1、首先保证文件系统能正常读写

    测试代码如下:

    #include <rtthread.h>
    #include <dfs_posix.h> /* 当需要使用文件操作时,需要包含这个头文件 */
    static void readwrite_sample(void)
    {
    int fd, size;
    char s[] = "RT-Thread Programmer!", buffer[80];
    rt_kprintf("Write string %s to test.txt.\\n", s);
    /* 以创建和读写模式打开 /text.txt 文件,如果该文件不存在则创建该文件*/
    fd = open("/text.txt", O_WRONLY | O_CREAT);
    if (fd >= 0)
    {
    write(fd, s, sizeof(s));
    close(fd);
    rt_kprintf("Write done.\\n");
    }
    /* 以只读模式打开 /text.txt 文件 */
    fd = open("/text.txt", O_RDONLY);
    if (fd >= 0)
    {
    size = read(fd, buffer, sizeof(buffer));
    close(fd);
    rt_kprintf("Read from file test.txt : %s \\n", buffer);
    if (size < 0)
    return ;
    }
    }
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(readwrite_sample, readwrite sample);

    2、在配置中打开文件后端

    在这里插入图片描述 构建以后工程中就会出现ulog_be.c文件,记得开启异步输出支持哦。

    在这里插入图片描述

    3、创建一个文件信息表 将文件信息填写完成

    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},
    };

    4、文件后端初始化

    // 日志文件后端配置注册表结构体
    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; // 日志写入缓存缓冲区大小
    };

    // 日志存储根目录:片内flash分区下log目录
    #define ROOT_PATH "/flash/log"
    // 单个日志文件最大容量:512KB
    #define FILE_SIZE 512 * 1024
    // 日志读写缓冲区大小:512字节
    #define BUFF_SIZE 512

    // 全局日志分类配置表,定义多套日志存储规则
    static struct _log_file table[] ={
    // 系统日志:最多10个文件,单文件512KB,缓冲区512字节
    {"sys" ,ROOT_PATH,10,FILE_SIZE,BUFF_SIZE},
    // 运动日志:最多5个文件,单文件512KB,缓冲区512字节
    {"motion" ,ROOT_PATH,5,FILE_SIZE,BUFF_SIZE},
    };

    // 运动日志专用TAG标识,用于日志过滤区分
    #define MOTION_TAG "MOVE"

    /**
    * @brief 系统日志后端过滤回调函数
    * @param backend ulog后端句柄
    * @param level 日志打印等级
    * @param tag 日志标签字符串
    * @param is_raw 是否原始日志
    * @param log 日志内容缓冲区
    * @param len 日志有效长度
    * @return rt_bool_t RT_TRUE-允许输出 RT_FALSE-过滤丢弃
    */

    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)
    {
    // 如果日志标签为MOVE运动日志,直接过滤不输出到sys系统日志文件
    if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)
    return RT_FALSE;
    else
    return RT_TRUE; // 其他标签日志正常输出
    }

    /**
    * @brief 运动日志后端过滤回调函数
    * @param backend ulog后端句柄
    * @param level 日志打印等级
    * @param tag 日志标签字符串
    * @param is_raw 是否原始日志
    * @param log 日志内容缓冲区
    * @param len 日志有效长度
    * @return rt_bool_t RT_TRUE-允许输出 RT_FALSE-过滤丢弃
    */

    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)
    {
    // 仅允许标签为MOVE的运动日志输出到motion日志文件
    if (rt_strncmp(tag,MOTION_TAG, sizeof(MOTION_TAG)) == 0)
    return RT_TRUE;
    else
    return RT_FALSE; // 其余日志全部过滤
    }

    // 系统日志ulog底层后端对象
    static struct ulog_backend sys_log_backend;
    // 系统日志文件输出后端实例
    static struct ulog_file_be sys_log_file;

    /**
    * @brief 初始化系统日志文件输出后端
    * @retval 无
    */

    void sys_log_file_backend_init(void)
    {
    struct ulog_file_be *file_be = &sys_log_file;
    // 绑定基础ulog后端对象
    file_be->parent = sys_log_backend;
    // 挂载系统日志专属过滤回调
    ulog_backend_filter_t filter = sys_log_file_backend_filter;
    // 根据配置表第0项(sys)初始化文件日志后端
    ulog_file_backend_init( file_be,
    table[0].name,
    table[0].dir_path,
    table[0].max_num,
    table[0].max_size,
    table[0].buf_size);
    // 启用该文件日志输出后端
    ulog_file_backend_enable(file_be);
    }

    // 运动日志ulog底层后端对象
    static struct ulog_backend motion_log_backend;
    // 运动日志文件输出后端实例
    static struct ulog_file_be motion_log_file;

    /**
    * @brief 初始化运动日志文件输出后端
    * @retval 无
    */

    void motion_log_file_backend_init(void)
    {
    struct ulog_file_be *file_be = &motion_log_file;
    // 绑定基础ulog后端对象
    file_be->parent = motion_log_backend;
    // 挂载运动日志专属过滤回调
    ulog_backend_filter_t filter = motion_log_file_backend_filter;
    // 根据配置表第1项(motion)初始化文件日志后端
    ulog_file_backend_init( file_be,
    table[1].name,
    table[1].dir_path,
    table[1].max_num,
    table[1].max_size,
    table[1].buf_size);
    // 启用该文件日志输出后端
    ulog_file_backend_enable(file_be);
    }

    /**
    * @brief 日志文件后端总初始化入口
    * @retval int 0-初始化成功
    */

    int file_backend_init(void)
    {
    // 初始化系统日志存储后端
    sys_log_file_backend_init();
    // 初始化运动日志存储后端
    motion_log_file_backend_init();
    return 0;
    }

    // RT-Thread自动初始化宏,系统启动时自动执行日志初始化函数
    INIT_APP_EXPORT(file_backend_init);

    5、运行查看

    在这里插入图片描述

    6、实现命令控制后端是否输出

    static void log_file_backend_cmd(uint8_t argc, char **argv)
    {
    if (argc < 3)
    {
    rt_kprintf("Usage: ulog_be_ctrl [name] [enable/disable]\\n");
    return;
    }

    const char *name = argv[1];
    const char *opt = argv[2];
    struct ulog_file_be *be = RT_NULL;

    // 匹配对应日志后端
    if (!rt_strcmp(name, table[0].name))
    be = &sys_log_file;
    else if (!rt_strcmp(name, table[1].name))
    be = &motion_log_file;
    else
    {
    rt_kprintf("Error: unknown backend %s\\n", name);
    return;
    }

    // 执行开关操作
    if (!rt_strcmp(opt, "disable"))
    {
    ulog_file_backend_disable(be);
    rt_kprintf("%s backend disabled\\n", name);
    }
    else if (!rt_strcmp(opt, "enable"))
    {
    ulog_file_backend_enable(be);
    rt_kprintf("%s backend enabled\\n", name);
    }
    else
    {
    rt_kprintf("Usage: ulog_be_ctrl [name] [enable/disable]\\n");
    }
    }
    MSH_CMD_EXPORT_ALIAS(log_file_backend_cmd, ulog_be_ctrl, control ulog file backend [name] [enable:disable]);

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » RT-Thread Ulog 组件使用详解:创建多个文件后端保存日志的方法
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址