2. RTOS日志基础

在RTOS环境中,日志系统与通用Linux或桌面系统有本质差异:RAM通常只有几十KB到几百KB,没有文件系统或仅有极简文件系统,任务栈空间有限,且日志代码本身不能长时间关中断或阻塞高优先级任务。因此,理解日志等级、格式、同步/异步输出与缓冲机制,是设计产品级日志系统的第一步。

一、日志等级:从“全开”到“只留错误”

日志等级是日志系统的第一道过滤网。常见等级从高到低为:ERROR、WARN、INFO、DEBUG、TRACE。等级越高,表示事件越严重、越需要保留;等级越低,表示信息越详细、越适合开发阶段使用。

在RTOS中,等级通常通过编译期宏和运行期变量双重控制。编译期宏用于裁剪代码体积,运行期变量用于现场动态调整。例如:

#define LOG_LEVEL_ERROR   0
#define LOG_LEVEL_WARN    1
#define LOG_LEVEL_INFO    2
#define LOG_LEVEL_DEBUG   3
#define LOG_LEVEL_TRACE   4

#ifndef LOG_COMPILE_LEVEL
#define LOG_COMPILE_LEVEL LOG_LEVEL_INFO
#endif

extern uint8_t g_log_runtime_level;

#define LOG_E(tag, fmt, ...) \
    do { if (LOG_LEVEL_ERROR <= LOG_COMPILE_LEVEL && \
             LOG_LEVEL_ERROR <= g_log_runtime_level) \
         log_output(LOG_LEVEL_ERROR, tag, fmt, ##__VA_ARGS__); } while(0)

这样,量产固件可把LOG_COMPILE_LEVEL设为LOG_LEVEL_WARN,只保留警告和错误;现场调试时再通过串口命令把g_log_runtime_level临时调到LOG_LEVEL_DEBUG,无需重新烧录。

二、日志格式:时间、等级、模块、内容

一条可读的RTOS日志通常包含四个字段:时间戳、等级、模块标签、消息体。时间戳可以来自SysTick、硬件定时器或RTC;模块标签用于区分驱动、协议栈、应用任务;消息体则承载具体参数和状态。

常见格式如下:

[  12345][I][NET ] dhcp bound, ip=192.168.1.20
[  12402][W][UART] rx overrun, drop=3
[  12510][E][FS  ] mount fail, err=-2

格式设计要兼顾可读性与解析效率。若日志需要上位机自动解析,建议采用固定宽度字段或结构化前缀,例如tick|level|tag|msg,避免在中断上下文中使用浮点格式化或动态内存分配。

三、同步与异步输出:阻塞与丢包的权衡

同步输出指调用LOG_I()后立即完成串口、SEGGER RTT或ITM的写入。优点是时序准确、实现简单;缺点是当输出外设慢于日志产生速度时,会阻塞当前任务,甚至在高优先级任务中造成实时性抖动。

异步输出指日志先写入环形缓冲区,由低优先级日志任务或DMA后台搬运到外设。优点是调用快、对实时任务影响小;缺点是缓冲区满时需要选择丢包、覆盖或阻塞策略,且日志时间戳可能略晚于实际事件。

在中断服务程序中,应只做“写入环形缓冲区”这一件事,绝不能在ISR中直接等待串口发送完成。异步日志是RTOS产品级日志的默认选择。

四、缓冲机制:环形缓冲区与多级缓冲

RTOS日志最常用的缓冲结构是单生产者单消费者环形缓冲区。生产者可能是多个任务或ISR,消费者是日志输出任务。写入时只移动写指针,读取时只移动读指针,配合临界区或原子操作即可实现无锁或轻量锁。

当单级缓冲不足时,可采用多级缓冲:第一级为ISR专用的短缓冲,第二级为任务级环形缓冲,第三级为DMA搬运缓冲。这样既能保证中断响应,又能平滑突发日志流量。

RTOS日志异步输出与缓冲数据流 任务/ISR LOG_I / LOG_E 环形缓冲区 write / read 指针 日志任务 低优先级搬运 输出外设 UART / RTT / ITM / Flash 缓冲策略对比 满时丢包:保留最新日志,适合现场诊断;覆盖旧数据:保留最近窗口,适合高频采样;阻塞等待:不丢日志但影响实时性。 建议:ISR 只写不等待;日志任务按优先级搬运;DMA 完成中断触发下一批发送。 同步输出 时序准、易阻塞 适合低速调试 异步输出 调用快、可丢包 产品级首选 多级缓冲 ISR短缓冲 + 任务环缓 + DMA 搬运

五、常见RTOS日志方案对比

不同RTOS和工具链提供了不同层次的日志能力。下表从输出方式、实时性、资源占用和适用场景四个维度进行对比:

方案 输出方式 实时性影响 资源占用 适用场景
printf 重定向 UART 同步阻塞 高,可能阻塞任务 低 裸机或极简RTOS调试
SEGGER RTT 异步,J-Link 读取 低 中,需J-Link 开发阶段高速日志
ARM ITM / SWO 异步,调试器读取 低 低,需SWO引脚 Cortex-M 开发调试
自建环形缓冲 + 日志任务 异步,可配UART/Flash 低,可控制丢包 中,需RAM和任务栈 产品级现场日志
Flash 日志文件系统 异步,掉电保存 中,写Flash耗时 高,需Flash和FS 黑匣子、故障回溯

六、案例与误区复盘

某工业网关项目在量产初期频繁出现“任务看门狗复位”,排查发现日志宏在中断中直接调用printf,而串口波特率仅115200,一条80字节日志耗时约7ms,导致高优先级控制任务被延迟。整改方案是:ISR只把日志写入256字节环形缓冲,日志任务以最低优先级搬运,缓冲区满时丢弃DEBUG级别日志,保留ERROR和WARN。整改后看门狗复位消失,现场仍能通过ERROR日志定位故障。

常见误区:一是认为“日志越多越好”,导致缓冲区频繁溢出和Flash磨损;二是在中断中调用带锁或动态内存的日志函数;三是忽略时间戳来源,多核或低功耗唤醒后时间戳错乱;四是量产固件未关闭DEBUG日志,既占带宽又暴露敏感信息。

总结来说,RTOS日志基础可以归纳为四句话:等级分层过滤,格式固定可解析,异步缓冲保实时,方案选型看场景。只有把等级、格式、同步/异步和缓冲机制配合好,日志系统才能在不破坏实时性的前提下,成为产品现场诊断的可靠依据。