RT-Thread学习日记(9) ulog日志

本文详细介绍了RTT系统中ulog日志组件的特性与使用方法,包括日志级别设置、标签分类、初始化流程、API使用示例等,旨在帮助开发者高效运用日志系统进行软件调试与维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

在RTT系统里引入日志系统,并将平时的日志写入sd卡内的文件系统

二、本文参考资料

1.https://www.rt-thread.org/document/site/programming-manual/ulog/ulog/

三、正文部分

3.1 ulog简介

日志的定义:日志是将软件运行的状态、过程等信息,输出到不同的介质中(例如:文件、控制台、显示屏等),并进行显示和保存。为软件调试、维护过程中的问题追溯、性能分析、系统监控、故障预警等功能,提供参考依据。可以说,日志的使用,几乎占用的软件生命周期的至少 80% 的时间。

日志的重要性:对于操作系统而言,由于其软件的复杂度非常大,单步调试在一些场景下并不适合,所以日志组件在操作系统上几乎都是标配。完善的日志系统也能让操作系统的调试事半功倍。

ulog 是一个非常简洁、易用的 C/C++ 日志组件,第一个字母 u 代表 μ,即微型的意思。它能做到最低ROM<1K, RAM<0.2K的资源占用。

日志输出的后端多样化,可支持例如:串口、网络,文件、闪存等后端形式。
日志输出被设计为线程安全的方式,并支持异步输出模式。
日志系统高可靠,在中断 ISR 、Hardfault 等复杂环境下依旧可用。
日志支持运行期 / 编译期设置输出级别。
日志内容支持按关键词及标签方式进行全局过滤。
API 和日志格式可兼容 linux syslog。
支持以 hex 格式 dump 调试数据到日志中。
兼容 rtdbg (RTT 早期的日志头文件)及 EasyLogger 的日志输出 API。

3.1.1 ulog 架构

下图为 ulog 日志组件架构图:
在这里插入图片描述
前端:该层作为离应用最近的一层,给用户提供了 syslog 及 LOG_X 两类 API 接口,方便用户在不同的场景中使用。
核心:中间核心层的主要工作是将上层传递过来的日志,按照不同的配置要求进行格式化与过滤然后生成日志帧,最终通过不同的输出模块,输出到最底层的后端设备上。
后端:接收到核心层发来的日志帧后,将日志输出到已经注册的日志后端设备上,例如:文件、控制台、日志服务器等等。

3.1.2 配置选项

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.1.2 日志级别

日志级别代表了日志的重要性,在 ulog 中由高到低,有如下几个日志级别:
在这里插入图片描述
在 ulog 中日志级别还有如下分类:

静态级别与动态级别:按照日志是否可以在运行阶段修改进行分类。可在运行阶段修改的称之为动态级别,只能在编译阶段修改的称之为静态级别。比静态级别低的日志(这里特指使用 LOG_X API 的日志)将不会被编译到 ROM 中,最终也不会输出、显示出来。而动态级别可以管控的是高于或等于静态级别的日志。在 ulog 运行时,比动态级别低的日志会被过滤掉。
全局级别与模块级别:按照作用域进行的分类。在 ulog 中每个文件(模块)也可以设定独立的日志级别。全局级别作用域大于模块级别,也就是模块级别只能管控那些高于或等于全局级别的模块日志。

综合上面分类可以看出,在 ulog 可以通过以下 4 个方面来设定日志的输出级别:

全局静态日志级别:在 menuconfig 中配置,对应 ULOG_OUTPUT_LVL 宏。
全局动态日志级别:使用 void ulog_global_filter_lvl_set(rt_uint32_t level) 函数来设定。
模块静态日志级别:在模块(文件)内定义 LOG_LVL 宏,与日志标签宏 LOG_TAG 定义方式类似。
模块动态日志级别:使用 int ulog_tag_lvl_filter_set(const char *tag, rt_uint32_t level) 函数来设定。

它们的作用范围关系为:全局静态>全局动态>模块静态>模块动态

3.1.3 日志标签

由于日志输出量的不断增大,为了避免日志被杂乱无章的输出出来,就需要使用标签(tag)给每条日志进行分类。标签的定义是按照模块化的方式
每条日志的标签属性也可以被输出并显示出来,同时 ulog 还可以设置每个标签(模块)对应日志的输出级别,当前不重要模块的日志可以选择性关闭,不仅降低 ROM 资源,还能帮助开发者过滤无关日志。

#define LOG_TAG     "example"     // 该模块对应的标签。不定义时,默认:NO_TAG
#define LOG_LVL     LOG_LVL_DBG   // 该模块对应的日志输出级别。不定义时,默认:调试级别
#include <ulog.h>                 // 必须在 LOG_TAG 与 LOG_LVL 下面

需要注意的,定义日志标签必须位于 #include <ulog.h> 的上方,否则会使用默认的 NO_TAG(不推荐定义在头文件中定义这些宏)。
日志标签的作用域是当前源码文件,项目源代码通常也会按照模块进行文件分类。所以在定义标签时,可以指定模块名、子模块名作为标签名称,这样不仅在日志输出显示时清晰直观,也能方便后续按标签方式动态调整级别或过滤。

3.2 日志初始化

3.2.1 初始化
int ulog_init(void)
//返 >=0 成功
//返 -5  失败,内存不足

在使用 ulog 前必须调用该函数完成 ulog 初始化。如果开启了组件自动初始化,该函数也将被自动调用。

3.2.2 去初始化
void ulog_deinit(void)

当 ulog 不再使用时,可以执行该 deinit 释放资源。

3.3 日志输出 API

ulog 主要有两种日志输出宏 API,源代码中定义如下所示:

#define LOG_E(...)                           ulog_e(LOG_TAG, __VA_ARGS__)				
#define LOG_W(...)                           ulog_w(LOG_TAG, __VA_ARGS__)
#define LOG_I(...)                           ulog_i(LOG_TAG, __VA_ARGS__)
#define LOG_D(...)                           ulog_d(LOG_TAG, __VA_ARGS__)
#define LOG_RAW(...)                         ulog_raw(__VA_ARGS__)
#define LOG_HEX(name, width, buf, size)      ulog_hex(name, width, buf, size)

宏 LOG_X(…):X 对应的是不同级别的第一个字母大写。参数 … 为日志内容,格式与 printf 一致。这种方式是首选,一方面因为其 API 格式简单,入参只有一个即日志信息,再者还支持按模块静态日志级别过滤。
宏 ulog_x(LOG_TAG, __ VA _ ARGS __):x对应的是不同级别的简写。参数LOG_TAG为日志标签,参数…` 为日志内容,格式与 printf 一致。这个 API 适用于在一个文件中使用不同 tag 输出日志的情况
在这里插入图片描述

3.4 日志使用示例

/*



*/

3.5 日志使用技巧

有了日志工具后,如果使用不当,也会造成日志被滥用、日志信息无法突出重点等问题。这里重点与大家分享下日志组件在使用时的一些技巧,让日志信息更加直观。主要关注点有:

合理利用标签分类

合理利用标签功能,每个模块代码在使用日志前,先明确好模块、子模块名称。这样也能让日志在最开始阶段就做好分类,为后期日志过滤也做好了准备。

合理利用日志级别

刚开始使用日志库时,大家会经常遇到 警告与错误 日志无法区分,信息与调试 日志无法区分,导致日志级别选择不合适。一些重要日志可能看不到,不重要的日志满天飞等问题。所以,在使用前务必仔细阅读日志级别小节,针对各个级别划分,里面有明确的标准。

避免重复性冗余日志

在一些情况下会出现代码的重复调用或者循环执行,多次输出相同、相似的日志问题。这样的日志不仅会占用很大的系统资源,还会影响开发人员对于问题的定位。所以,在遇到这种情况时,建议增加对于重复性日志特殊处理,比如:让上层来输出一些业务有关的日志,底层只返回具体结果状态;同一个时间点下相同的日志,是否可以增加去重处理,在错误状态没有变化时,只输出一次等等。

开启更多的日志格式

ulog 默认的日志格式中没有开启时间戳及线程信息。这两个日志信息,在 RTOS 上挺实用。它们能帮助开发者直观的了解各个日志的运行时间点、时间差,还能清晰的看到是在哪个线程执行当前代码。所以如果条件允许,还是建议开启。

关闭不重要的日志

ulog 提供了多种维度的日志开关、过滤的功能,完全能够做到精细化控制,所以如果在调试某个功能模块时,可以适当关闭其他无关模块的日志输出,这样就可以聚焦在当前调试的模块上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值