RT-Thread: ulog 日志 讲解和使用

说明:记录 RT-Thread: ulog 日志功能和使用流程。

官网资料链接:

https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/ulog/ulog

1.ulog 简介


        日志的定义:日志是将软件运行的状态、过程等信息,输出到不同的介质中(例如:文件、控制台、显示屏等),并进行显示和保存。为软件调试、维护过程中的问题追溯、性能分析、系统监控、故障预警等功能,提供参考依据。可以说,日志的使用,几乎占用的软件生命周期的至少 80% 的时间。
1.日志输出的后端多样化,可支持例如:串口、网络,文件、闪存等后端形式。
2.日志输出被设计为线程安全的方式,并支持异步输出模式。
3.日志系统高可靠,在中断 ISR 、Hardfault 等复杂环境下依旧可用。
4.日志支持运行期 / 编译期设置输出级别。
5.日志内容支持按关键词及标签方式进行全局过滤。
6.API 和日志格式可兼容 linux syslog。
7.支持以 hex 格式 dump 调试数据到日志中。
8.兼容 rtdbg (RTT 早期的日志头文件)及 EasyLogger 的日志输出 API。

2.日志功能开启

点开 settings 菜单

点亮,ulog 日志 图标,

注意:没有开启这个图标框架功能时, LOG_D 这些函数的基本 串口打印输出 功能还是有的,只是一些丰富的信息没有了。

右键点击 详细配置 有更丰富的配置选项

RT-Thread: ulog 日志 讲解和使用_第1张图片

设置好后点击保存。

3.日志功能介绍

3.1日志级别

日志级别代表了日志的重要性,在 ulog 中由高到低,有如下几个日志级别:

级别

名称

描述

LOG_LVL_ASSERT

断言

发生无法处理、致命性的的错误,以至于系统无法继续运行的断言日志

LOG_LVL_ERROR

错误

发生严重的、不可修复的错误时输出的日志属于错误级别日志

LOG_LVL_WARNING

警告

出现一些不太重要的、具有可修复性的错误时,会输出这些警告日志

LOG_LVL_INFO

信息

给本模块上层使用人员查看的重要提示信息日志,例如:初始化成功,当前工作状态等。该级别日志一般在量产时依旧保留

LOG_LVL_DBG

调试

给本模块开发人员查看的调试日志,该级别日志一般在量产时关闭

3.2日志标签

        由于日志输出量的不断增大,为了避免日志被杂乱无章的输出出来,就需要使用标签(tag)给每条日志进行分类。标签的定义是按照模块化的方式,例如:Wi-Fi 组件包括设备驱动(wifi_driver)、设备管理(wifi_mgnt)等模块,则 Wi-Fi 组件内部模块可以使用 wifi.driver、wifi.mgnt 等作为标签,进行日志的分类输出。

        每条日志的标签属性也可以被输出并显示出来,同时 ulog 还可以设置每个标签(模块)对应日志的输出级别,当前不重要模块的日志可以选择性关闭,不仅降低 ROM 资源,还能帮助开发者过滤无关日志。

参见 rt-thread\examples\ulog_example.c ulog 例程文件,在文件顶部有定义 LOG_TAG 宏:

#define LOG_TAG     "example"     // 该模块对应的标签。不定义时,默认:NO_TAG
#define LOG_LVL     LOG_LVL_DBG   // 该模块对应的日志输出级别。不定义时,默认:调试级别
#include                  // 必须在 LOG_TAG 与 LOG_LVL 下面复制错误复制成功

        需要注意的,定义日志标签必须位于 #include  的上方,否则会使用默认的 NO_TAG(不推荐定义在头文件中定义这些宏)。

        日志标签的作用域是当前源码文件,项目源代码通常也会按照模块进行文件分类。所以在定义标签时,可以指定模块名、子模块名作为标签名称,这样不仅在日志输出显示时清晰直观,也能方便后续按标签方式动态调整级别或过滤。

3.3 日志初始化

初始化

int ulog_init(void)复制错误复制成功

返回

描述

>=0

成功

-5

失败,内存不足

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

去初始化

void ulog_deinit(void)复制错误复制成功

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

3.4 日志输出 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 输出日志的情况。

API

描述

LOG_E(...)

错误级别日志

LOG_W(...)

错误级别日志

LOG_I(...)

提示级别日志

LOG_D(...)

调试级别日志

LOG_RAW(...)

输出 raw 日志

LOG_HEX(name, width, buf, size)

输出 16 进制格式数据到日志

        LOG_X 及 ulog_x 这类 API 输出都是带格式日志,有些时候需要输出不带任何格式的日志时,可以使用 LOG_RAW 或 ulog_raw() 。例如:

LOG_RAW("\r");
ulog_raw("\033[2A");

        以 16 进制 hex 格式 dump 数据到日志中可使用可以使用 LOG_HEX() 或 ulog_hex 。函数参数及描述如下所示:

参数

描述

tag

日志标签

width

一行 hex 内容的宽度(数量)

buf

待输出的数据内容

size

数据大小

        hexdump 日志为 DEBUG 级别,支持运行期的级别过滤,hexdump 日志对应的 tag ,支持运行期的标签过滤。

        ulog 也提供里断言 API :ASSERT(表达式) ,当断言触发时,系统会停止运行,内部也会执行 ulog_flush() ,所有日志后端将执行 flush 。如果开启了异步模式,缓冲区中所有的日志也将被 flush 。断言的使用示例如下:

void show_string(const char *str)
{
    ASSERT(str);
    ...
}

4.日志使用示例

4.0 输出API

API

描述

LOG_E(...)

错误级别日志

LOG_W(...)

错误级别日志

LOG_I(...)

提示级别日志

LOG_D(...)

调试级别日志

LOG_RAW(...)

输出 raw 日志

LOG_HEX(name, width, buf, size)

输出 16 进制格式数据到日志

4.1使用示例

下面将以 ulog 例程进行介绍,打开 rt-thread\examples\ulog_example.c 可以看到,顶部有定义该文件的标签及静态优先级。

#define LOG_TAG              "example"
#define LOG_LVL              LOG_LVL_DBG
#include 复制错误复制成功

        在 void ulog_example(void) 函数中有使用 LOG_X API ,大致如下:

/* output different level log by LOG_X API */
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);复制错误复制成功

这些日志输出 API 均支持 printf 格式,并且会在日志末尾自动换行。

        以下截图是 如下代码放在 main 函数中运行后的效果,截图是 MobaXterm1_CHS1.exe 串口调试软件的显示效果

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#define LED_2   GET_PIN(C,3)
#include 

int main(void)
{
    int count = 1;
    while (count++)
    {
        rt_thread_mdelay(2000);
        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);

    }
    return RT_EOK;
}

RT-Thread: ulog 日志 讲解和使用_第2张图片

        可以看到每条日志都是按行显示,不同级别日志也有着不同的颜色。在日志最前面有当前系统的 tick ,中间有显示日志级别及标签,最后面是具体的日志内容。

在本文后面也会重点介绍这些日志格式及配置说明。

4.2在中断 ISR 中使用

        很多时候需要在中断 ISR 中输出日志,但是中断 ISR 可能会打断正在进行日志输出的线程。要保证中断日志与线程日志互不干涉,就得针对于中断情况进行特殊处理。

        ulog 已集成中断日志的功能,但是默认没有开启,使用时打开 Enable ISR log 选项即可,日志的 API 与线程中使用的方式一致,例如:

#define LOG_TAG              "driver.timer"
#define LOG_LVL              LOG_LVL_DBG
#include 

void Timer2_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    LOG_D("I'm in timer2 ISR");

    /* leave interrupt */
    rt_interrupt_leave();
}

        这里说明下中断日志在 ulog 处于同步模式与异步模式下的不同策略:
        -同步模式下:如果线程此时正在输出日志时来了中断,此时如果中断里也有日志要输出,会直接输出到控制台上,不支持输出到其他后端; 
        -异步模式下:如果发生上面的情况,中断里的日志会先放入缓冲区中,最终和线程日志一起交给日志输出线程来处理。

4.3 hexdump 输出使用

        hexdump 也是日志输出时较为常用的功能,通过 hexdump 可以将一段数据以 hex 格式输出出来,对应的 API 为:void ulog_hexdump(const char *tag, rt_size_t width, rt_uint8_t *buf, rt_size_t size) ,

下面看下具体的使用方法及运行效果:

参数

描述

tag

日志标签

width

一行 hex 内容的宽度(数量)

buf

待输出的数据内容

size

数据大小

/* 定义一个 128 个字节长度的数组 */
uint8_t i, buf[128];
/* 在数组内填充上数字 */
for (i = 0; i < sizeof(buf); i++)
{
    buf[i] = i;
}
/* 以 hex 格式 dump 数组内的数据,宽度为 16 */
ulog_hexdump("buf_dump_test", 16, buf, sizeof(buf));

可以将上面的代码拷贝到 ulog 例程中运行,然后再看下实际运行结果:

RT-Thread: ulog 日志 讲解和使用_第3张图片

5.设置日志格式

点开 settings 菜单

点亮,ulog 日志 图标,

注意:没有开启这个图标框架功能时, LOG_D 这些函数的基本 串口打印输出 功能还是有的,只是一些丰富的信息没有了。

右键点击 详细配置 有更丰富的配置选项

RT-Thread: ulog 日志 讲解和使用_第4张图片

具体设置内容

RT-Thread: ulog 日志 讲解和使用_第5张图片

分别可以配置:

浮点型数字的支持(传统的 rtdbg/rt_kprintf 均不支持浮点数日志)、

带颜色的日志、

时间信息(包括时间戳)、

级别信息、

标签信息、

线程信息。

设置完成后点击保存

你可能感兴趣的:(RT-Thread,RT-Thread)