3. 实现引导日志分级
任务目标
在 Zephyr 引导程序中实现分级日志输出:根据编译期日志级别宏,控制 printk 输出的详细程度,使开发者可在调试与发布构建间切换,而无需修改业务代码。
实现方案
通过自定义日志宏 BOOT_LOG,将日志级别映射到 Zephyr 的 LOG_LEVEL 配置。核心思路:
- 定义
LOG_LEVEL_ERR/WRN/INF/DBG四个级别,数值越大越详细; - 在编译期用
IS_ENABLED判断当前级别,决定printk是否参与编译; - 级别宏在
prj.conf中通过BOOT_LOG_LEVEL配置,实现构建时切换。
完整代码
/* boot_log.h */
#ifndef BOOT_LOG_H
#define BOOT_LOG_H
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#define LOG_LEVEL_ERR 1
#define LOG_LEVEL_WRN 2
#define LOG_LEVEL_INF 3
#define LOG_LEVEL_DBG 4
/* 默认级别:警告 */
#ifndef BOOT_LOG_LEVEL
#define BOOT_LOG_LEVEL LOG_LEVEL_WRN
#endif
/* 编译期级别比较 */
#define BOOT_LOG_AT_LEVEL(level) \
(IS_ENABLED(CONFIG_BOOT_LOG_LEVEL_ENABLED) && \
(BOOT_LOG_LEVEL >= (level)))
/* 分级输出宏 */
#define BOOT_LOG_ERR(...) \
do { if (BOOT_LOG_AT_LEVEL(LOG_LEVEL_ERR)) { printk("[ERR] "); printk(__VA_ARGS__); printk("\n"); } } while (0)
#define BOOT_LOG_WRN(...) \
do { if (BOOT_LOG_AT_LEVEL(LOG_LEVEL_WRN)) { printk("[WRN] "); printk(__VA_ARGS__); printk("\n"); } } while (0)
#define BOOT_LOG_INF(...) \
do { if (BOOT_LOG_AT_LEVEL(LOG_LEVEL_INF)) { printk("[INF] "); printk(__VA_ARGS__); printk("\n"); } } while (0)
#define BOOT_LOG_DBG(...) \
do { if (BOOT_LOG_AT_LEVEL(LOG_LEVEL_DBG)) { printk("[DBG] "); printk(__VA_ARGS__); printk("\n"); } } while (0)
#endif /* BOOT_LOG_H */
/* main.c */
#include <zephyr/kernel.h>
#include "boot_log.h"
void main(void)
{
BOOT_LOG_ERR("boot failed: %d", -EIO);
BOOT_LOG_WRN("retry count: %d", 3);
BOOT_LOG_INF("booting from slot %d", 1);
BOOT_LOG_DBG("flash addr: 0x%x", 0x10000);
}
/* prj.conf */
CONFIG_BOOT_LOG_LEVEL_ENABLED=y
/* 可选值: 1=ERR, 2=WRN, 3=INF, 4=DBG */
/* 通过 CMake 或命令行覆盖: -DBOOT_LOG_LEVEL=4 */
构建与运行
- 将上述三个文件放入 Zephyr 应用目录(如
boot_log_demo/); - 编译(以
qemu_cortex_m3为例):
west build -b qemu_cortex_m3 . -DBOOT_LOG_LEVEL=4
west build -t run
验证方法
观察串口输出,应看到全部四条日志(ERR/WRN/INF/DBG)。随后将 -DBOOT_LOG_LEVEL 改为 2 重新编译运行,此时仅输出 [ERR] 与 [WRN] 两条,[INF] 与 [DBG] 不再出现。若改为 1,则仅输出 [ERR]。通过不同级别下输出条目的增减,即可确认分级生效。
关键说明
- 级别判断在编译期完成,未达级别的
printk调用不会生成代码,零运行时开销; CONFIG_BOOT_LOG_LEVEL_ENABLED作为总开关,置n时所有日志被完全裁剪;- 实际引导工程中,可将
BOOT_LOG_LEVEL与构建系统变量绑定,实现 release/debug 自动切换。