一、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 实际使用建议

聊了这么多,最后给你几个实在的建议:

  1. 日志级别要分层——开发阶段用DEBUG,测试阶段用INFO,发布阶段用NOTICE以上。别嫌麻烦,这能省很多事。
  2. 分类标识要规划好——项目一开始就定好分类规则。别等到代码写了几万行再回头加,那工作量……嗯,你懂的。
  3. 存储机制要权衡——有文件系统就用文件,没有就用内存缓冲区。如果条件允许,两个都开。
  4. 别忘了清理——日志轮转一定要配。我见过一个项目,日志文件配了无限大,结果跑了半年,磁盘满了,系统直接挂了。

好了,关于QNX日志系统的架构、级别、分类和存储机制,今天就聊到这。下一章咱们会讲怎么用slog2infoslog2conf这些工具来实际操作。到时候我会拿一个真实项目里的例子,手把手带你走一遍。