在嵌入式开发中,日志模块在日常开发和调试定位中扮演着非常重要的角色,好的日志工具能辅助工程师快速了解固件运行状态、错误定位、流程跟踪等。本文将介绍一个新的日志框架:defmt(deferred formatting,延迟格式化)。
众所周知,GDB 是许多嵌入式开发人员的首选开发和调试工具,但是在许多实时性较高、或需要长期调试运行的场景则显得不那么方便,因此固件的状态通常通过串口或网络实时发送给主机,如嵌入式 C 工程师经常使用的EasyLogger就能使用多种单片机外设或存储日志,也能显示日志级别,格式化输出等。但是如果大量使用日志来输出运行状态,则会占用一些的 flash 空间以及 CPU 时间。
defmt则做了非常棒的优化,defmt使用单片机内部 rtt 外设来作为输出端。同时能节省大量的格式化带来的空间浪费和时间浪费。
有哪些优势?
不占用太多空间
defmt优化了格式化字符串和代码路径字符串等只读数据段名称,这些字符串会被编译到 elf 文件但不会包含到 bin 文件,因此格式化显示的过程在主机段完成,因此需要解析固件的 elf 文件才能正常打印出。同时单片机无需发送额外的格式化的字符串,只需打印显示的数据内容以及当前日志的格式化索引即可。因此对于重度依赖日志的系统可以大量使用而不会消耗太多的flash空间。
不消耗额外时间
defmt的日志流是输出到内存缓存中,有主机主动读取,因此不会消耗单片机太多的 IO 读写资源。因此同时由于每条日志仅仅只需输出日志索引和需要打印的变量内容,无需再输出格式化字符串和代码位置字符串,因此不会消耗太多的时间,但是极高优化了日志的传输,让日志生产端和日志消费端都能快速读写。对于高实时性的场景,不会再出现添加日志则无法复现异常 bug 或添加日志会导致其他 bug 的问题。
日志级别设置
defmt有 5 个日志等级,分别是trace,debug,info,warn,error,覆盖所有等级的日志输出,通过环境变量RUST_LOG或DEFMT_LOG指定日志的等级。
颜色显著提示
时间戳展示
可以使能#[timestamp]属性开启时间戳打印功能。方便定位日志间代码运行的所号的时间
日志位于文件中的位置标记
再 debug 模式中,每套日志都会显示该条日志所在的文件名路径以及行数,再 vscode 中点击该字符串能快速定位到该行代码,快速查看日志的上下文环境。
轻松集成到项目
基于 Rust 的 cargo 管理工具,只需再 toml 中添加依赖即可,无需其他移植工作。
支持中途在线查看日志
通过命令probe-rs attach即可查看日志,无需重启单片机,且能立即查看到历史日志,对于紧急查看日志状态 时,需要了解之前的日志分析 bug 非常有用。