【Zephyr|ESP32-S3】基础学习:使用LOG系统完成 “Hello World!“打印
【Zephyr|ESP32-S3】基础学习:使用LOG系统完成 "Hello World!"打印
哈喽,我是余火,一个普通的牛马打工人,目前正在学如何使用Zephyr RTOS。
上一篇完成了 Hello World 的编译烧录,用 printf 每秒打印一次系统运行时间。跑起来没问题,但你有没有注意到——串口输出里只有你自己写的文本内容,没有任何时间戳、模块名、日志等级这些信息。工程小的时候无所谓,等代码模块一多,满屏 printf 根本分不清哪条是谁打的。
Zephyr 自带一套生产级的日志子系统 LOG,支持时间戳、模块名、日志分级过滤,用起来也不比 printf 复杂多少。今天就把它替换上去。
改了哪些东西
在上一篇的 hello_world 工程上直接改,一共 2 个文件:
| 文件 | 改了什么 |
|---|---|
prj.conf |
新增 CONFIG_LOG=y + 日志级别 |
src/main.c |
添加 LOG 头文件和模块注册,printf → LOG_INF |
为什么要用 LOG?
先看 Zephyr 下三种打印方式的区别:
| 方案 | 输出效果 | 适用场景 |
|---|---|---|
printf |
Hello World! (uptime: 1000 ms) |
纯文本,无时间戳无模块信息 |
printk |
[00:00:01.000,000] <inf> Hello World |
内核早期启动、底层调试 |
LOG系统 |
[00:00:01.000,000] <inf> main: Hello World! (board: esp32s3_devkitc, uptime: 1000 ms) |
带时间戳+模块名+等级标签 |
简单说:
- printf:最通用,但输出是"裸文本",什么上下文都没有
- printk:内核自带,开销极低,适合 Boot 阶段 LOG 子系统还没就绪时用
- LOG:功能最全,支持按模块名、日志等级过滤,多模块项目必备
💡 实际开发中,printf 直接淘汰,printk 只在极少数底层场景出现,LOG 覆盖 95% 以上的打印需求。
启用 LOG
LOG 子系统需要通过 Kconfig 启用。在 prj.conf 里加两行:
#--- Enable log system ---#
CONFIG_LOG=y
# 指定默认日志级别为INFO
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_LOG=y 会让构建系统自动编译 LOG 核心,不需要额外配置硬件驱动——LOG 底层复用的是串口输出,串口在第 2 篇就已经能用了。
注册 LOG 模块
打开 src/main.c,添加头文件和模块注册:
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);
LOG_MODULE_REGISTER(name, level) 是每个用到 LOG 的 .c 文件都必须写的一行:
name— 模块名称,会出现在日志输出的前缀里(比如上面写的main,输出就是main:开头)level— 此模块的编译时日志级别,一般直接引用CONFIG_LOG_DEFAULT_LEVEL
日志等级
Zephyr 的 LOG 有 5 个等级,级别从低到高:
| 宏 | 等级 | 含义 | 对应函数 |
|---|---|---|---|
LOG_LEVEL_NONE |
0 | 禁用 | — |
LOG_LEVEL_ERR |
1 | 仅错误 | LOG_ERR() |
LOG_LEVEL_WRN |
2 | 警告+错误 | LOG_WRN() |
LOG_LEVEL_INF |
3 | 信息+警告+错误 | LOG_INF() |
LOG_LEVEL_DBG |
4 | 全部输出 | LOG_DBG() |
等级是过滤门槛:设置为 3(INFO),则 LOG_INF、LOG_WRN、LOG_ERR 都会输出,LOG_DBG 在编译期直接被裁掉,零运行开销。
这样你调试时把级别改成 4 就能看到所有日志,发布时改回 3 只保留关键信息,一行配置切换,不用逐条注释代码。
替换 printf
把 main 函数里的 printf 全部换成 LOG_INF:
int main(void)
{
LOG_INF("Hello World from Log! (board: %s)", CONFIG_BOARD_TARGET);
for(;;) {
LOG_INF("Hello World from Log! (uptime: %u ms)", k_uptime_get_32());
k_sleep(K_SECONDS(1));
}
return 0;
}
用法和 printf 一样,支持 %s、%d、%u 等格式化占位符。
改完后在 Workbench 中重新编译:

烧录后打开串口助手,可以看到 LOG 系统的输出效果——每条日志都带有时间戳、日志等级标签和模块名前缀:

对比上一篇的 printf 输出,信息量直接上了一个台阶:哪个模块、什么时间、什么级别,一目了然。
常见问题
Q1:编译报错 undefined reference to 'logX'
• 解决:prj.conf 漏了 CONFIG_LOG=y,或者忘了在 .c 文件里写 LOG_MODULE_REGISTER。
Q2:LOG 输出没有时间戳
• 解决:需要同时开启 CONFIG_LOG_TIMESTAMP_24HR=y 或 CONFIG_LOG_TIMESTAMP_64BIT=y(根据平台选择),一般 CONFIG_LOG=y 会自动拉取。
Q3:想临时看调试日志,不想改 prj.conf
• 解决:编译时通过 menuconfig 或 CMake 参数 -DCONFIG_LOG_DEFAULT_LEVEL=4 临时覆盖,不用修改源文件。
从这篇开始,后面所有代码都会用 LOG 代替 printf,LOG 也是 Zephyr 官方示例和驱动代码里最主流的打印方式。
希望我的笔记能对你有一点点点的帮助!欢迎关注一起学习👇
更多推荐


所有评论(0)