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 这些函数的基本 串口打印输出 功能还是有的,只是一些丰富的信息没有了。

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

设置好后点击保存。

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 <ulog.h>                 // 必须在 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 <ulog.h>复制错误复制成功

        在 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 <rtdbg.h>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;
}

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

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

4.2在中断 ISR 中使用

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

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

#define LOG_TAG              "driver.timer"
#define LOG_LVL              LOG_LVL_DBG
#include <ulog.h>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 例程中运行,然后再看下实际运行结果:

5.设置日志格式

点开 settings 菜单

点亮,ulog 日志 图标,

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

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

具体设置内容

分别可以配置:

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

带颜色的日志、

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

级别信息、

标签信息、

线程信息。

设置完成后点击保存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/613879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海外市场调研为什么要用独享静态代理IP?

独享静态IP在海外市场调研中扮演着至关重要的角色&#xff0c;提供了一系列无可比拟的优势。独享静态代理IP的稳定性和可靠性对于长期的市场调研至关重要&#xff0c;它保证了连接的持续性和数据的准确性。通过这些方面的综合优势&#xff0c;独享静态代理IP成为海外市场调研中…

工业智能网关:HiWoo Box远程采集设备数据

工业智能网关&#xff1a;HiWoo Box远程采集设备数据 在工业4.0和智能制造的浪潮下&#xff0c;工业互联网已成为推动产业升级、提升生产效率的关键。而在这其中&#xff0c;工业智能网关扮演着至关重要的角色。今天&#xff0c;我们就来深入探讨一下工业智能网关。 一、什么…

用Linux的视角来理解缓冲区概念

缓冲区的认识 缓冲区&#xff08;buffer&#xff09;是存储数据的临时存储区域。当我们用C语言向文件中写入数据时&#xff0c;数据并不会直接的写到文件中&#xff0c;中途还经过了缓冲区&#xff0c;而我们需要对缓冲区的数据进行刷新&#xff0c;那么数据才算写到文件当中。…

Java获取IP地址及对应的归属地

目录 前言 一、获取访问的IP地址 二、通过IP地址获取对应的归属地 2.1 Ip2region 2.1.1 高达 99.9 % 的查询准确率 2.1.2 Ip2region V2.0 特性 2.1.3 多语言以及查询客户端的支持 2.2 Ip2region xdb Java 查询客户端实现 2.2.1 引入 Maven 仓库 2.2.2 ip2region.xdb …

【.NET Core】可为null类型详解

【.NET Core】可为null类型详解 文章目录 【.NET Core】可为null类型详解一、概述二、可为空的值类型2.1 声明和赋值2.2 检查可为空值类型2.3 基础类型与可为空的值类型互换2.4 可为空的值类型装箱和取消装箱2.5 如何确定可为空的值类型 三、可为 null 的引用类型 一、概述 nu…

用通俗易懂的方式讲解:在 Langchain 中建立一个多模态的 RAG 管道

写在前面 语言模型的出现彻底改变了我们从文件中提取信息的方式。然而&#xff0c;我们知道图片&#xff0c;通常是图表和表格&#xff0c;经常包含关键信息&#xff0c;但基于文本的语言模型无法处理媒体文件。 例如&#xff0c;我们以前只能使用 PDF 文件中的文本来查找答案…

C#编程-实现线程声明周期

实现线程声明周期 当System.Threading.Thread类的对象被创建的时候,线程的生命周期开始。线程的生命周期在完成任务时结束。在线程的生命周期中有各种状态。这些状态是: 未启动状态可运行状态不可运行状态死亡状态下图显示了线程的各种状态和引起线程从一个状态变为另一个状…

欢乐的周末 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 小华和小为是很要好的朋友,他们约定周末一起吃饭。 通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达)。求小华和小为都能到达的聚餐地点有多少个? 输入描述 第一行输入m和n,m代表…

C练习——递归求第n个人年龄

题目&#xff1a; 有n个人坐在一起&#xff0c;第n个人比第n-1个人大2岁&#xff0c;第n-1个人比第n-2个人大2岁&#xff0c;以此类推&#xff0c;……&#xff0c;第1个人是10岁。请问第n个人年龄多大&#xff1f; 解析&#xff1a; 简单循环也能求解 但按题意要求递归求解…

Spark SQL进阶

DataFrame详解 清洗相关API 去重API 删除空缺值的API 替换缺失值的API from pyspark import SparkConf, SparkContext import os from pyspark.sql import SparkSession# 绑定指定的Python解释器 os.environ[SPARK_HOME] /export/server/spark os.environ[PYSPARK_PYTHON]…

渗透线应用-取料呼叫FC(SCL源代码)

渗透线应用相关文章可以参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/135526725https://rxxw-control.blog.csdn.net/article/details/135526725渗透线小车控制 https://rxxw-control.blog.csdn.net/article/details/133611151

【算法】基础算法001之双指针

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.数组分块&#xf…

【JaveWeb教程】(20) MySQL数据库开发之 基本查询、条件查询、聚合函数、分组查询、排序查询、分页查询 详细代码示例讲解

目录 1. 数据库操作-DQL1.1 介绍1.2 语法1.3 基本查询1.4 条件查询1.5 聚合函数1.6 分组查询1.7 排序查询1.8 分页查询1.9 案例1.9.1 案例一1.9.2 案例二 在上次学习的内容中&#xff0c;我们讲解了&#xff1a; 使用DDL语句来操作数据库以及表结构&#xff08;数据库设计&…

C++学习笔记(三十二):c++ 堆内存与栈内存比较

本节对堆和栈内存进行描述。 应用程序启动后&#xff0c;操作系统将整个程序加载到内存&#xff0c;分配相应的物理ram&#xff0c;确保程序可以正常运行。堆和栈是ram中存在的两个区域。栈通常是一个预定义大小的内存区域&#xff0c;一般是2M字节左右。堆也是预定了默认值的…

12、JVM高频面试题

1、JVM的主要组成部分有哪些 JVM主要分为下面几部分 类加载器&#xff1a;负责将字节码文件加载到内存中 运行时数据区&#xff1a;用于保存java程序运行过程中需要用到的数据和相关信息 执行引擎&#xff1a;字节码文件并不能直接交给底层操作系统去执行&#xff0c;因此需要…

NumPy 数据操作实用指南:从基础到高效(下)

文章接上篇&#xff1a; In [53]: from PIL import Image In [60]: dog Image.open(./dog.jpg) dog . . . In [61]: dog_datanp.array(dog) # 图片数据是ndarray # 彩色照片三维&#xff1a;高度&#xff0c;宽度&#xff0c;像素&#xff08;表示不同颜色&#xff09;&…

C语言操作符与表达式详解

目录 操作符的分类&#xff1a; &#xff08;1&#xff09;算数操作符 &#xff08;2&#xff09;移位操作符 &#xff08;3&#xff09;位操作符 &#xff08;4&#xff09;赋值操作符 &#xff08;5&#xff09;单目操作符 &#xff08;6&#xff09;关系操作符 &…

CSS 选择器全攻略:从入门到精通(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

pytorch11:模型加载与保存、finetune迁移训练

目录 一、模型加载与保存1.1 序列化与反序列化概念1.2 pytorch中的序列化与反序列化1.3 模型保存的两种方法1.4 模型加载两种方法 二、断点训练2.1 断点保存代码2.2 断点恢复代码 三、finetune3.1 迁移学习3.2 模型的迁移学习3.2 模型微调步骤3.2.1 模型微调步骤3.2.2 模型微调…

Asp .Net Core 系列: 集成 CORS跨域配置

文章目录 什么是CORS?Asp .Net Core 种如何配置CORS?CorsPolicyBuilder类详解注册以及使用策略三种方式EnableCors 和 DisableCors 特性关于带证书与不带证书代码的实现跨源&#xff08;cross-origin&#xff09;不带请求证书(Credentials)跨源&#xff08;cross-origin&…