第2章:日志系统基础——嵌入式日志架构、级别与输出方式
做嵌入式开发,尤其是搞OTA升级和异常复位排查,日志系统就是你的“黑匣子”。
我见过太多同事,设备死机了,手里连个像样的日志都没有,只能靠猜。说实话,那感觉就像在黑暗里找一根针。所以,这一章我们先把地基打牢——搞懂嵌入式日志系统到底该怎么搭。
2.1 嵌入式日志系统架构
一个成熟的日志系统,不是简单往串口扔几个printf就完事了。它得有层次,有分工。
我个人习惯把日志系统分成三层:
- 采集层:负责从代码里把日志信息抓出来。比如你写的LOG_DEBUG("value = %d", val),这就是采集。
- 处理层:对日志进行格式化、加时间戳、过滤级别。这层决定了日志好不好读。
- 输出层:把处理好的日志送到目的地——串口、文件、网络,或者内存缓冲区。
你想想看,如果这三层混在一起,后期想改输出方式,那得改多少代码?
核心原则:采集与输出解耦。代码里只写日志宏,底层输出交给驱动层去管。
下面这张图,是我常用的日志系统架构,你可以参考一下:
2.2 日志级别:DEBUG / INFO / WARN / ERROR / FATAL
级别这东西,说白了就是给日志分个三六九等。为什么要分?因为嵌入式设备的存储和带宽都有限,你不能什么都往日志里塞。
我见过一个项目,OTA升级失败后,日志文件里全是DEBUG信息,真正的错误原因被淹没了。嗯,这就是级别没用好。
下面这个表格,是我在项目中常用的级别定义:
| 级别 | 数值 | 含义 | 典型场景 |
|---|---|---|---|
| DEBUG | 0 | 调试信息,开发阶段使用 | 变量值打印、函数入口/出口 |
| INFO | 1 | 正常流程信息 | OTA开始下载、校验通过 |
| WARN | 2 | 警告,不影响当前功能 | 重试次数超限、校验和警告 |
| ERROR | 3 | 错误,功能受影响 | 下载超时、文件损坏 |
| FATAL | 4 | 致命错误,系统可能复位 | 看门狗超时、内存越界 |
我的习惯:发布版本把日志级别设为INFO及以上。DEBUG信息只在开发阶段打开。这样既能保证问题可追溯,又不会把存储空间撑爆。
代码里怎么用?我一般这样定义宏:
/* 日志级别定义 */
#define LOG_LEVEL_DEBUG 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_WARN 2
#define LOG_LEVEL_ERROR 3
#define LOG_LEVEL_FATAL 4
/* 当前编译级别,发布时改为 LOG_LEVEL_INFO */
#define CURRENT_LOG_LEVEL LOG_LEVEL_DEBUG
/* 日志宏 */
#define LOG_DEBUG(fmt, ...) \
do { \
if (CURRENT_LOG_LEVEL <= LOG_LEVEL_DEBUG) \
log_output("DEBUG", fmt, ##__VA_ARGS__); \
} while(0)
#define LOG_INFO(fmt, ...) \
do { \
if (CURRENT_LOG_LEVEL <= LOG_LEVEL_INFO) \
log_output("INFO", fmt, ##__VA_ARGS__); \
} while(0)
/* WARN / ERROR / FATAL 类似,省略 */
注意:我曾经在一个项目里,把FATAL级别的日志放在了断言之前。结果设备复位了,日志还没来得及刷出去。后来我改成:FATAL日志先强制刷缓冲区,再触发复位。这个顺序很重要。
2.3 日志输出方式:UART / 文件 / 网络
输出方式的选择,取决于你的硬件资源和应用场景。我按优先级排个序:
2.3.1 UART输出
最基础,也最常用。开发阶段基本靠它。优点是简单,缺点是速度慢,而且占用一个串口。
我记得有一次,OTA升级过程中串口日志太多,导致主任务被阻塞。后来加了DMA传输,问题才解决。
2.3.2 文件输出
适合有文件系统的设备。日志写到Flash或SD卡里,掉电不丢失。排查异常复位时,这个最有用。
但要注意:频繁写Flash会磨损存储介质。我一般用环形缓冲区,攒够一定量再批量写入。
2.3.3 网络输出
适合联网设备。日志通过UDP或TCP上报到服务器。优点是实时性强,缺点是一旦网络断开,日志就丢了。
我建议:网络日志作为辅助,本地文件日志作为主备份。两条腿走路,才稳当。
下面这个表格,对比了三种方式的优劣:
| 输出方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| UART | 实现简单,实时性好 | 速度慢,占用硬件资源 | 开发调试、小数据量 |
| 文件 | 掉电保存,容量大 | 写入慢,有磨损 | 异常复位分析、OTA日志 |
| 网络 | 远程查看,实时性强 | 依赖网络,可能丢包 | 远程运维、大规模部署 |
实战建议:OTA升级过程中,同时开启UART和文件输出。UART用于实时监控,文件用于事后分析。如果设备支持网络,再加一路网络上报。三路并行,万无一失。