一、QNX系统日志概述:日志系统架构、日志级别、日志分类、日志存储机制
做嵌入式调试这么多年,我有个习惯——拿到一个系统,第一件事就是看它的日志怎么管。QNX的日志系统,说实话,跟Linux那套不太一样。它更轻量,也更贴近实时系统的需求。今天咱们就把它掰开揉碎了聊一聊。
1.1 日志系统架构
QNX的日志系统,说白了就是一套“生产者-消费者”模型。生产者是各个进程,消费者是日志记录器。中间有个核心组件叫slogger2,它负责收日志、分类、然后分发。
我画个简单的逻辑图给你看:
进程A(产生日志) → slogger2(核心服务) → 日志文件 / 控制台 / 网络
进程B(产生日志) → slogger2(核心服务) → 日志文件 / 控制台 / 网络
进程C(产生日志) → slogger2(核心服务) → 日志文件 / 控制台 / 网络
每个进程通过调用slog2系列的API,把日志消息发给slogger2。然后slogger2根据配置,决定这些日志去哪。你可以写到本地文件,也可以输出到串口,甚至通过网络发到远程日志服务器。
核心要点:QNX的日志架构是异步的。进程发完日志就继续干活,不阻塞。这一点对实时系统特别重要。你想想看,如果写个日志还要等磁盘I/O,那实时性就全毁了。
1.2 日志级别
QNX的日志级别,我习惯把它比作“警报的严重程度”。从最轻的调试信息,到最严重的致命错误,一共分这么几档:
| 级别 | 数值 | 含义 | 我一般怎么用 |
|---|---|---|---|
| SHUTDOWN | 0 | 系统即将关闭 | 几乎不用,除非系统要挂了 |
| CRITICAL | 1 | 严重错误 | 系统无法继续运行的情况 |
| ERROR | 2 | 一般错误 | 功能异常,但系统还能跑 |
| WARNING | 3 | 警告 | 潜在问题,比如内存快不够了 |
| NOTICE | 4 | 重要通知 | 状态变化,比如服务启动成功 |
| INFO | 5 | 一般信息 | 日常运行记录 |
| DEBUG1~DEBUG3 | 6~8 | 调试信息 | 开发阶段用,上线后关掉 |
嗯,这里要注意。DEBUG级别有三个,DEBUG1、DEBUG2、DEBUG3。我个人的习惯是:DEBUG1放函数入口出口,DEBUG2放中间变量值,DEBUG3放循环内部细节。这样调试时可以按需打开,不会一下子被日志淹死。
我的小技巧:在产品发布前,我会把日志级别统一调到NOTICE以上。DEBUG级别的日志全部关掉。这样既能保证关键信息不丢,又不会因为日志太多影响性能。我曾经见过一个项目,DEBUG日志没关,结果系统跑着跑着磁盘满了……那叫一个惨。
1.3 日志分类
光有级别还不够。你想想看,一个系统里可能有几十个模块在跑。如果所有日志都混在一起,找起来跟大海捞针似的。所以QNX引入了“日志分类”的概念。
每个日志消息可以带一个分类标识。这个标识是个32位的整数,你可以自己定义。比如:
#define CATEGORY_DRIVER_I2C 0x0001
#define CATEGORY_DRIVER_SPI 0x0002
#define CATEGORY_NETWORK 0x0010
#define CATEGORY_APP_MAIN 0x0100
然后你在slogger2的配置文件里,可以指定只收集某些分类的日志。比如我只想看I2C驱动的问题,那就只开CATEGORY_DRIVER_I2C。其他模块的日志一概不收。
这样做的好处很明显:
- 减少干扰——调试某个模块时,不会被其他模块的日志刷屏
- 节省资源——不需要的日志直接丢弃,不占CPU和存储
- 便于分析——日志文件里全是相关的内容,不用再手动过滤
我在项目中遇到过一个问题:系统偶尔会卡顿,但日志里全是网络模块的INFO信息,把真正的错误给淹没了。后来我加了分类过滤,只保留驱动层的WARNING以上日志,问题很快就定位到了——一个中断处理函数里有个死循环。
1.4 日志存储机制
日志存到哪?怎么存?存多久?这些问题在嵌入式系统里特别关键。QNX提供了几种存储方式:
1.4.1 本地文件存储
最常用的方式。日志写到文件系统里,比如/var/log/slog2.log。你可以设置文件大小上限,超过后自动轮转。比如:
# slogger2配置示例
log_file = /var/log/slog2.log
log_file_size = 1048576 # 1MB
log_file_count = 5 # 保留5个历史文件
这样日志文件最大也就5MB,不会把磁盘撑爆。我一般建议生产环境这么配。
1.4.2 内存缓冲区
有些系统没有文件系统,或者对写寿命有要求(比如用eMMC或NAND Flash)。这时候可以用内存缓冲区。日志存在RAM里,掉电就丢。但好处是速度快,不磨损存储介质。
QNX的slogger2支持环形缓冲区模式。缓冲区满了之后,新的日志会覆盖最旧的日志。你可以通过slog2info命令随时查看缓冲区内容。
注意:内存缓冲区模式有个坑——如果系统崩溃了,缓冲区里的日志也会跟着丢。我曾经吃过这个亏。后来我学乖了,在关键系统上会同时开内存缓冲区和文件存储。内存缓冲区用来实时查看,文件存储用来做事后分析。
1.4.3 远程日志
QNX还支持把日志通过网络发到远程服务器。协议可以是UDP或者TCP。远程日志的好处是:
- 日志不占本地存储
- 可以集中管理多个设备的日志
- 即使设备挂了,日志也已经发出去了
不过要注意网络开销。如果日志量太大,可能会影响实时性。我一般只在调试阶段用远程日志,生产环境还是老老实实写本地文件。
1.5 实际使用建议
聊了这么多,最后给你几个实在的建议:
- 日志级别要分层——开发阶段用DEBUG,测试阶段用INFO,发布阶段用NOTICE以上。别嫌麻烦,这能省很多事。
- 分类标识要规划好——项目一开始就定好分类规则。别等到代码写了几万行再回头加,那工作量……嗯,你懂的。
- 存储机制要权衡——有文件系统就用文件,没有就用内存缓冲区。如果条件允许,两个都开。
- 别忘了清理——日志轮转一定要配。我见过一个项目,日志文件配了无限大,结果跑了半年,磁盘满了,系统直接挂了。
好了,关于QNX日志系统的架构、级别、分类和存储机制,今天就聊到这。下一章咱们会讲怎么用slog2info和slog2conf这些工具来实际操作。到时候我会拿一个真实项目里的例子,手把手带你走一遍。