这里写目录标题
- 一、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 组件是一个设计精良、功能全面的嵌入式日志解决方案。通过本文的介绍,您应该已经掌握了:
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]);
