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 核心逻辑流程图

下面这张图展示了日志从节点产生到最终落地的完整路径:

ROS2日志重定向核心流程 ROS2节点 日志级别过滤 重定向? 终端输出 文件输出 终端 + 文件 (tee) 图例: 节点 过滤 决策 输出 双输出

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。这样不管是按时间还是按模块排查,都一目了然。

好了,关于日志重定向就聊这么多。记住一句话:日志是自动驾驶系统的记忆,别让它失忆。

可可精品资料库