4. ros2 run与日志重定向:将日志输出到文件、终端与文件同时输出
日志这东西,说白了就是自动驾驶系统的「黑匣子」。我在项目里见过太多人,车跑着跑着突然报错,终端刷屏刷得飞起,等想回头查问题的时候,发现日志早就被冲没了。嗯,今天我们就来聊聊怎么管好这些日志。
4.1 为什么需要日志重定向?
ROS2默认的日志输出,是直接怼到终端上的。你想想看,一个自动驾驶系统跑起来,传感器数据、控制指令、状态信息,每秒可能输出几十上百条日志。终端就那么大一屏,滚动起来根本看不清。
我个人习惯是:调试阶段看终端,运行阶段存文件,关键问题两边都要。为什么?因为终端日志方便实时观察,文件日志方便事后复盘。有一次我在测试路径规划模块,车突然在路口停了3秒,终端上只闪过一条WARN级别的日志。要不是我同时把日志写到了文件里,根本来不及翻回去看具体是什么原因。
核心原则:日志重定向不是「要不要」的问题,而是「怎么用」的问题。生产环境必须落盘,调试环境建议双输出。
4.2 基础操作:ros2 run的日志输出
先看最基础的用法。启动一个节点时,日志默认会打印到终端:
ros2 run my_autonomous_package path_planner_node
这样跑起来,所有INFO、WARN、ERROR级别的日志都会出现在终端。但问题来了——终端关闭后,日志就没了。我在早期做原型验证时吃过这个亏,调了一下午的参数,晚上想复盘,发现终端早就关了,啥也没留下。
4.3 将日志输出到文件
ROS2的日志系统其实很灵活。它支持通过环境变量来控制日志的输出行为。最常用的就是 ROS_LOG_DIR 和 ROS_LOG_FILENAME。
举个例子,我想把日志存到 /var/log/autonomous/ 目录下:
export ROS_LOG_DIR=/var/log/autonomous/
ros2 run my_autonomous_package path_planner_node
这样日志就会自动写到 /var/log/autonomous/ 下面,文件名默认是 节点名_时间戳.log 的格式。
如果你想自定义文件名,可以这样:
export ROS_LOG_FILENAME=path_planner_2024.log
ros2 run my_autonomous_package path_planner_node
小技巧:我建议在启动脚本里统一设置日志目录。比如我们团队的做法是,每个模块的日志按日期分文件夹,方便按时间线排查问题。
4.4 终端与文件同时输出
只输出到文件,终端就看不到实时信息了。调试的时候很不方便。那怎么办?用tee命令。
Linux的 tee 命令可以把标准输出同时写到文件和终端。ROS2的日志默认走的是标准错误流(stderr),所以我们要把stderr也重定向一下:
ros2 run my_autonomous_package path_planner_node 2>&1 | tee /var/log/autonomous/path_planner.log
解释一下:
2>&1:把标准错误流重定向到标准输出流| tee 文件路径:把输出同时写到文件和终端
这样你就能在终端看到实时日志,同时文件里也有一份完整的记录。我曾经用这个方式排查过一个传感器数据丢帧的问题——终端上看到偶尔跳一下的WARN日志,翻文件才发现每30秒规律性地出现一次,最终定位到是DMA缓冲区配置太小。
注意:如果日志量特别大(比如激光雷达点云处理节点),tee命令可能会拖慢终端响应。我建议在调试阶段用,生产环境直接写文件就好。
4.5 使用ROS2的日志级别控制
有时候你不想看到所有日志。比如调试阶段只想看WARN和ERROR,INFO太多太吵。ROS2支持通过命令行参数控制日志级别:
ros2 run my_autonomous_package path_planner_node --ros-args --log-level WARN
这样只有WARN及以上级别的日志才会输出。我个人习惯是:
- 开发调试:INFO级别,配合tee双输出
- 集成测试:WARN级别,只写文件
- 生产运行:ERROR级别,写文件+告警通知
4.6 日志重定向的完整方案
在实际项目中,我一般会写一个启动脚本,把日志管理封装好。下面是一个典型的例子:
#!/bin/bash
# 日志配置
LOG_DIR=/var/log/autonomous/$(date +%Y%m%d)
LOG_FILE=${LOG_DIR}/path_planner.log
NODE_NAME=path_planner_node
# 创建日志目录
mkdir -p ${LOG_DIR}
# 设置ROS2日志环境变量
export ROS_LOG_DIR=${LOG_DIR}
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity}] [{time}] [{name}]: {message}"
# 启动节点,双输出
ros2 run my_autonomous_package ${NODE_NAME} \
--ros-args --log-level INFO \
2>&1 | tee ${LOG_FILE}
这里我还自定义了日志的输出格式 RCUTILS_CONSOLE_OUTPUT_FORMAT,加上时间戳和日志级别,方便后续分析。
4.7 核心逻辑流程图
下面这张图展示了日志从节点产生到最终落地的完整路径:
4.8 常见问题与避坑指南
问题1:日志文件太大怎么办?
我曾经在一个连续运行72小时的测试中,发现日志文件涨到了2GB。打开文件都卡半天。解决方案是加日志轮转(log rotation)。ROS2本身不支持自动轮转,但你可以用Linux的 logrotate 工具:
/var/log/autonomous/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
}
这样每天轮转一次,保留最近7天的日志,旧的自动压缩。
问题2:日志里中文乱码?
嗯,这个我踩过坑。ROS2的日志默认用UTF-8编码,但有些终端模拟器不兼容。建议在启动脚本里强制设置:
export LANG=zh_CN.UTF-8
export LC_ALL=zh_CN.UTF-8
问题3:多节点同时写同一个日志文件?
千万别这么干!ROS2的日志系统不是线程安全的,多个进程同时写一个文件会导致日志错乱。每个节点应该有自己的日志文件,或者用 ROS_LOG_DIR 自动按节点名分文件。
我的经验:在自动驾驶项目中,我习惯把日志按「日期/模块/节点名」三层目录组织。比如 /logs/2024-01-15/perception/lidar_node.log。这样不管是按时间还是按模块排查,都一目了然。
好了,关于日志重定向就聊这么多。记住一句话:日志是自动驾驶系统的记忆,别让它失忆。