日志系统基础:ROS2日志系统架构、日志级别、输出格式与存储位置
各位同学好,我是老张。今天咱们聊聊ROS2的日志系统。说实话,我刚开始接触Autoware那会儿,最头疼的就是出了问题不知道从哪查起。后来慢慢摸透了日志系统,才发现——嗯,这东西其实就是你的“行车记录仪”,把车跑过的每一步都记下来了。
日志系统,说白了就是ROS2框架里内置的一套消息记录机制。它不像你写代码时自己加的print,而是有级别、有格式、有存储的一套完整体系。我个人习惯,一上车先看日志,比看什么可视化工具都管用。
ROS2日志系统架构
ROS2的日志系统,底层用的是spdlog这个高性能C++日志库。上层呢,ROS2封装了一套rclcpp/rclpy的接口,让你在节点里直接调用。整个架构分三层:
- 日志前端:就是你代码里写的
RCLCPP_INFO或者rclpy.logging.get_logger().info()。这部分负责接收你的日志消息。 - 日志核心:rcl层,负责日志级别过滤、格式化、分发。这里决定了你的日志能不能被输出。
- 日志后端:默认输出到控制台,也可以配置输出到文件、网络等。Autoware里我们通常同时输出到控制台和文件。
我在项目中遇到过一个问题:明明代码里写了日志,但控制台就是看不到。后来发现是日志级别没配对,核心层直接给过滤掉了。你想想看,排查了半天,结果是自己把日志关掉了,多冤。
核心要点:ROS2日志系统是分层架构,前端只管发,核心管过滤,后端管输出。理解这个,你才知道日志“消失”了该查哪一层。
日志级别:从DEBUG到FATAL
ROS2定义了5个日志级别,从低到高分别是:
| 级别 | 数值 | 含义 | 典型场景 |
|---|---|---|---|
| DEBUG | 10 | 调试信息 | 变量值、循环次数、临时中间结果 |
| INFO | 20 | 普通信息 | 节点启动、参数加载、正常状态切换 |
| WARN | 30 | 警告 | 参数异常、降级运行、非致命错误 |
| ERROR | 40 | 错误 | 传感器断连、算法失败、超时 |
| FATAL | 50 | 致命错误 | 内存耗尽、核心组件崩溃、系统不可恢复 |
这里有个关键点:日志级别是“向上包含”的。什么意思?如果你把日志级别设为WARN,那WARN、ERROR、FATAL都会输出,但DEBUG和INFO就被过滤掉了。我建议你在开发阶段用DEBUG,跑仿真用INFO,真车上路用WARN。为什么呢?因为真车上日志太多会影响实时性,你想想看,每毫秒刷几百条日志,CPU都去干这个了,还怎么算路径规划?
我的小技巧:在Autoware的launch文件里,我习惯加一个参数 log_level:=debug,调试完再改回 warn。这样不用改代码,启动时指定就行。
日志输出格式解析
ROS2默认的日志输出格式长这样:
[INFO] [1712345678.123456789] [my_node]: Hello, this is an info message
拆开来看,一共四个部分:
- [INFO]:日志级别,告诉你这条消息有多严重。
- [1712345678.123456789]:时间戳,单位是秒,精确到纳秒。这个在排查时序问题时特别有用。我记得有一次定位模块和规划模块时间对不上,就是靠这个时间戳发现差了0.5秒。
- [my_node]:节点名称,告诉你这条日志是哪个节点发的。Autoware里节点很多,没有这个你根本不知道是谁在说话。
- Hello, this is an info message:消息正文,就是你想说的内容。
你也可以自定义格式。比如我个人喜欢加上文件名和行号:
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity}] [{time}] [{name}]: {function_name}() at {file_name}:{line_number} - {message}"
这样输出就变成了:
[INFO] [1712345678.123456789] [my_node]: main() at /home/user/my_node.cpp:42 - Hello, this is an info message
嗯,这个在追代码的时候特别爽,看到日志就知道是哪一行出的问题,不用再去grep了。
注意:自定义格式会增加日志输出的开销。在性能敏感的场景下,建议保持默认格式,或者只在DEBUG级别下启用详细格式。
日志文件存储位置
ROS2默认只把日志输出到控制台,不写文件。但Autoware里我们肯定要落盘,不然重启后日志就没了,你拿什么复盘?
ROS2提供了日志文件存储机制,默认路径是:
~/.ros/log/
里面会按启动时间生成文件夹,比如:
~/.ros/log/2025-01-15-14-30-22-123456/
├── my_node-1.log
├── another_node-2.log
└── ...
每个节点一个日志文件。文件名格式是 节点名-进程ID.log。
你也可以自己指定路径,通过环境变量:
export ROS_LOG_DIR=/data/autoware_logs
我在项目中遇到过一个问题:跑了一整天,日志文件把磁盘塞满了,导致系统卡死。后来我加了个日志轮转策略,每天只保留最近7天的日志。具体做法是在启动脚本里加一句:
export RCUTILS_LOG_FILE_MAX_SIZE=10485760 # 单个文件最大10MB
export RCUTILS_LOG_FILE_ROTATION=5 # 最多保留5个轮转文件
这样每个节点的日志最多占50MB,整个系统跑一天也就几百MB,完全可控。
避坑指南:我曾经因为没配日志轮转,在实车测试时磁盘写满,导致规划模块异常退出。从那以后,我每次部署前都会检查日志存储配置。你也不希望因为日志把车搞停了吧?
好了,日志系统的基础就这些。记住一句话:日志是你的第一道防线,出了问题先看日志,别急着改代码。下一节咱们聊聊怎么用这些日志去排查Autoware里的常见故障。