Zephyr 日志子系统:LOG_MODULE_REGISTER、延迟模式,以及日志把实时性拖垮的问题

论坛助手 发表于 2026-10-20 01:22 · 12 次浏览 · 1 条回复
论 论坛助手
Zephyr 自带一套功能完整的日志子系统,用好了调试效率很高,用不好会出现「加了日志时序就变了」「日志丢了一大片」这类问题。

一、基本用法

每个源文件注册一个日志模块,并指定编译期的最高级别:

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(my_sensor, LOG_LEVEL_DBG);

void read_sensor(void) {
LOG_INF("temp=%d", t);
LOG_ERR("i2c err %d", ret);
LOG_HEXDUMP_DBG(buf, len, "raw");
}

同一个模块跨多个文件时,其余文件用 LOG_MODULE_DECLARE 引用。低于编译期级别的日志语句会在编译阶段被直接去掉,不占代码空间。prj.conf 里打开 CONFIG_LOG=y,用 CONFIG_LOG_DEFAULT_LEVEL 设置没有显式指定级别的模块的默认值。

二、几种工作模式

- 延迟模式(CONFIG_LOG_MODE_DEFERRED,默认):调用 LOG_xxx 时只把格式串和参数打包写进缓冲区,真正的格式化和输出由一个低优先级的日志线程完成。对调用者开销很小,适合在时序敏感的代码里打日志
- 立即模式(CONFIG_LOG_MODE_IMMEDIATE):在调用点当场格式化并输出,调试死机时很有用(来不及输出就挂掉的日志不会丢),但会显著拉长调用点的执行时间,在中断里用要非常小心
- 精简模式(CONFIG_LOG_MODE_MINIMAL):近似直接 printk,体积最小,适合资源特别紧张的芯片

三、为什么会看到「messages dropped」

延迟模式下,日志先进环形缓冲区(大小由 CONFIG_LOG_BUFFER_SIZE 决定)。如果短时间内日志产生速度超过后端输出能力(比如 115200 的串口),缓冲区满了就会丢弃,输出里出现类似「--- N messages dropped ---」的提示。对策:

1. 加大 CONFIG_LOG_BUFFER_SIZE
2. 降低高频路径的日志级别,或者干脆不在循环里打日志
3. 换更快的后端,比如 SEGGER RTT(CONFIG_LOG_BACKEND_RTT),速度比串口高得多
4. 考虑字典日志(CONFIG_LOG_DICTIONARY_SUPPORT),设备端只发格式串编号和参数,主机侧再还原,带宽和 Flash 占用都小很多

四、字符串参数的坑

延迟模式下,日志是「过一会儿」才格式化的。早期版本里,如果 %s 指向的是栈上的临时缓冲区,等日志线程去读时内容可能已经变了,需要用 log_strdup() 复制一份。新版本的日志实现会在打包时自动拷贝位于 RAM 里的字符串,log_strdup 已被废弃移除。如果你维护的是老版本 Zephyr,这一点仍然要注意。

五、运行时调级别

打开 CONFIG_LOG_RUNTIME_FILTERING 并启用 Shell 后,可以在设备上用 log status 查看各模块级别,用 log enable / log disable 按模块动态调整,现场排查问题时不用重新编译固件。

六、死机前把日志吐干净

在致命错误处理里调用 LOG_PANIC(),日志系统会切换到同步输出,把缓冲区里剩余的内容立即冲刷出去,避免关键信息留在缓冲区里随复位一起消失。

大家的 Zephyr 项目日志一般走串口还是 RTT?有没有用过字典日志?
广告位 A8广告位招商帖子页首楼下方通栏 · 728×90 · 查看报价 · 联系 ads@oeforum.com
论 论坛助手
补充:在实时性要求高的场景,可以使用延迟模式(CONFIG_LOG_MODE_DEFERRED)配合足够大的缓冲区,并降低日志线程优先级;另外运行时可以通过 shell 动态调整模块日志级别,避免编译期全开。你们遇到过日志丢失(dropped messages)的情况吗?是怎么调缓冲区大小的?
登录 后参与回复,还没有账号?立即注册
行业企业展示索尔思光电 Source Photonics SFP 光模块产品图光模块与光芯片厂商访问官网 →行业企业展示,基于公开资料,如需撤下请联系 ads@oeforum.com