# 로깅 및 출력 제어 옵션

ROS2에서 로깅(logging)은 노드 및 애플리케이션 상태를 이해하는 데 필수적인 기능이다. 로깅 수준, 출력 방식, 로그 파일 저장 경로 등 다양한 옵션을 활용하여 런치(Launch) 파일에서 동작을 통제할 수 있다. 여기에서는 ROS2 Humble 버전 기준으로 노드 및 런치 시스템의 로깅 옵션을 살펴보고, 다양한 출력 제어 방식을 소개한다.

#### 로깅 레벨의 이해

ROS2의 로깅 레벨은 크게 다섯 가지가 존재한다. 각 레벨별 의미는 다음과 같다.

* **DEBUG**: 디버그 목적의 메시지를 출력한다. 내부 동작 확인이나 세부 분석이 필요한 경우 사용한다.
* **INFO**: 일반적인 정보 메시지를 출력한다. 보통 상태 확인이나 처리 완료 로그 등을 나타낸다.
* **WARN**: 경고 메시지를 출력한다. 치명적이지 않은 문제이지만 주의가 필요한 상황에서 사용한다.
* **ERROR**: 오류 메시지를 출력한다. 동작 흐름에 치명적일 수 있는 오류나 예외 상황을 나타낸다.
* **FATAL**: 치명적인 문제가 발생했음을 나타낸다. 보통 시스템을 중단하거나 긴급 조치가 필요한 상황에서 사용한다.

각 로그 레벨은 정수에 대응되며, 내부적으로 우선순위를 가지고 있다. 일반적으로 ROS2는 $\text{DEBUG} < \text{INFO} < \text{WARN} < \text{ERROR} < \text{FATAL}$ 순으로 취급한다.

#### 런치 파일에서 노드 로깅 레벨 제어

런치 시스템에서 노드를 실행할 때, 로깅 레벨을 지정하여 노드가 출력할 최소 로그 레벨을 정할 수 있다. 예를 들어 Python 기반 런치 파일에서 `Node` 액션을 활용할 때 아래와 같이 설정한다.

```python
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_package',
            executable='my_node',
            name='example_node',
            output='screen',
            arguments=['--ros-args', '--log-level', 'INFO'],
        )
    ])
```

`--ros-args --log-level INFO`는 노드 실행 시 INFO 레벨 이상의 로그만 출력하도록 강제한다. 이를 DEBUG, WARN, ERROR 등으로 변경하면 해당 레벨 이상의 메시지만 보이게 된다.

#### 런치 시스템 자체 로깅과 노드 로깅

ROS2 런치 시스템에서도 자체 로깅 기능이 있다. 런치 시스템이 생성한 로그와 노드 내부의 로그는 서로 다른 계층에서 발생한다. 따라서 런치 파일에서 노드 로깅 레벨을 바꾸더라도 런치 자체의 로그 레벨은 별도로 설정해야 한다. 예를 들어 런치 파일의 실행 시 `--debug` 옵션을 주면 런치 시스템에서 발생하는 로그 메시지가 상세하게 출력된다.

* 노드 로깅 설정: `Node` 액션의 인자로 `--ros-args --log-level ...` 전달.
* 런치 로깅 설정: `$ launch_service --debug` 와 같이 런치 자체에 대한 디버그 옵션 부여.

#### 출력 방식을 제어하는 옵션

ROS2 런치에서 노드 실행 시 `output` 인자를 통해 로그 출력 위치를 정할 수 있다. 대표적으로 아래의 옵션을 사용할 수 있다.

* **screen**: 터미널 콘솔에 직접 출력
* **log**: ROS2 로그 파일(기본적으로 `$HOME/.ros/log/` 디렉터리)로 기록
* **both**: 콘솔과 로그 파일 두 곳에 모두 기록

일반적으로 디버깅 중에는 `screen` 옵션을 통해 바로바로 메시지를 모니터링하고, 운영 환경에서는 `log` 혹은 `both`로 설정하는 편이 좋다.

#### 환경 변수 기반 로깅 설정

ROS2는 다양한 환경 변수를 통해 로깅 동작을 제어할 수 있다. 예를 들어 `$RCL_LOG_LEVEL` 환경 변수를 지정하면, 전체 노드에 대한 로깅 레벨을 일괄적으로 설정할 수 있다. 다음과 같은 방식으로 쉘에서 환경 변수를 세팅한다.

```bash
export RCL_LOG_LEVEL=WARN
```

이렇게 설정하면 모든 노드가 WARN 이상 레벨의 메시지만 출력하게 된다. 이는 런치 파일이나 특정 노드 별 설정보다 우선할 수 있으며, 서로 충돌하는 경우(런치 파일에서 다른 레벨을 지정)는 노드 생성 시점의 설정이 우선 적용되는지 또는 환경 변수가 우선 적용되는지 확인이 필요하다. 일반적으로 노드 실행 인자 설정이 환경 변수보다 더 구체적인 설정으로 간주된다.

#### ROS2 로그 파일 구조와 관리

ROS2의 기본 로그 파일 구조는 주로 `$HOME/.ros/log/` 디렉터리 안에 생성된다. ROS2 런치 파일을 통해 노드를 실행할 때, `output='log'`를 지정하면 해당 노드가 생성하는 로그는 자동으로 해당 경로에 기록된다. 로그 파일은 일반적으로 다음과 같은 정보로 구성된다.

* **타임스탬프**: 로그가 기록된 시간 정보
* **노드 이름**: 어느 노드가 로그를 생성했는지 식별
* **로그 레벨**: DEBUG, INFO, WARN, ERROR, FATAL 중 어느 수준인지
* **메시지 본문**: 실제 로깅하고자 하는 텍스트

기본 동작은 노드별로 폴더가 분리되거나 하는 구조가 아니므로, 필요한 경우 특정 노드만 별도 로그로 보관하고 싶다면 각 노드에 대해 로그 경로 혹은 파일명을 분리 지정해야 한다. 이러한 설정은 보통 런치 파이썬 코드에서 `Node` 액션의 `additional_env` 옵션 등을 활용하여 이루어질 수 있다.

#### 런치에서 특정 노드별 로그 경로 설정

런치 파일에서 특정 노드만 별도의 로그 파일로 저장하려면, 노드 실행 환경 변수를 재정의하는 방식으로 접근할 수 있다. 예를 들어 `$ROS_LOG_DIR` 환경 변수를 노드별로 다르게 주입하여, 서로 다른 디렉터리에 로그가 생성되도록 구성한다.

```python
from launch import LaunchDescription
from launch.actions import SetEnvironmentVariable
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        SetEnvironmentVariable('ROS_LOG_DIR', '/tmp/ros_logs/node_a'),
        Node(
            package='my_package',
            executable='node_a',
            name='node_a',
            output='log',
        ),
        SetEnvironmentVariable('ROS_LOG_DIR', '/tmp/ros_logs/node_b'),
        Node(
            package='my_package',
            executable='node_b',
            name='node_b',
            output='log',
        ),
    ])
```

위 예시는 `node_a`가 `/tmp/ros_logs/node_a` 경로에 로그를 남기도록 하고, `node_b`는 `/tmp/ros_logs/node_b` 경로에 로그를 남기도록 설정한다. 단, 이를 위해서는 각 `Node` 액션 전후로 `SetEnvironmentVariable`을 적절히 배치해 주어야 한다는 점에 주의한다.

#### rclcpp / rclpy 로깅 API 활용

노드 내부에서는 C++의 경우 `rclcpp::Logger`를 통해, Python의 경우 `rclpy.logging`을 통해 로그를 남긴다. 예를 들어 C++ 노드에서의 기본 형태는 다음과 같다.

```cpp
#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("example_node");

  RCLCPP_INFO(node->get_logger(), "This is an INFO log message.");
  RCLCPP_WARN(node->get_logger(), "This is a WARN log message.");

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

Python 노드에서는 아래와 같은 형태로 로깅을 수행한다.

```python
import rclpy
from rclpy.node import Node

class ExampleNode(Node):
    def __init__(self):
        super().__init__('example_node')
        self.get_logger().info('This is an INFO log message.')
        self.get_logger().warn('This is a WARN log message.')

def main(args=None):
    rclpy.init(args=args)
    node = ExampleNode()
    rclpy.spin(node)
    rclpy.shutdown()
```

코드 단에서 로깅 레벨을 변경하거나 노드마다 다른 로거 이름을 부여하여 로그를 정교하게 관리할 수도 있다.

#### 런치 파이썬에서 동적 로깅 레벨 제어

런치 시스템은 파이썬 스크립트이므로, 특정 조건에 따라 동적으로 로그 레벨을 설정할 수도 있다. 예컨대 디버그 모드, 개발 모드, 운영 모드 등의 플래그에 따라 다른 로깅 레벨을 적용하려면, 런치 파일 내부에서 분기 처리를 통해 `--log-level` 옵션을 주입하면 된다.

```python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node

def generate_launch_description():
    log_level_arg = DeclareLaunchArgument(
        'log_level',
        default_value='INFO',
        description='Logging level of the node'
    )
    log_level = LaunchConfiguration('log_level')

    return LaunchDescription([
        log_level_arg,
        Node(
            package='my_package',
            executable='my_node',
            name='my_dynamic_node',
            output='screen',
            arguments=['--ros-args', '--log-level', log_level],
        )
    ])
```

이렇게 구성해 두면 런치 실행 시 아래와 같은 형태로 원하는 로깅 레벨을 지정할 수 있다.

```bash
ros2 launch my_package my_launch.py log_level:=WARN
```

개발 및 테스트 중에는 `DEBUG`를 사용하고, 운영 환경에서는 `WARN` 이상만 출력하도록 간단히 전환할 수 있다.

#### 고급 로깅 활용: Logger 이름 지정

ROS2의 로거는 노드마다 기본적으로 노드 이름을 사용한다. 그러나 하나의 노드 안에서도 서로 다른 주제나 컴포넌트별로 로그를 나누고 싶다면, 별도의 로거 이름을 직접 부여할 수 있다. 예를 들어 C++에서는 `rclcpp::Node` 객체를 사용하되 다른 로거를 선언하여 사용한다.

```cpp
#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("example_node");

  // 노드의 기본 로거를 사용
  RCLCPP_INFO(node->get_logger(), "This is an INFO log using the default logger.");

  // 새로운 로거 이름 'custom_logger'를 선언 후 사용
  auto custom_logger = rclcpp::get_logger("custom_logger");
  RCLCPP_WARN(custom_logger, "This is a WARN log using custom_logger.");

  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

이처럼 `rclcpp::get_logger("로거_이름")` 형태로 로거를 생성하여 사용할 수 있다. 이를 통해 로그 메시지가 어느 모듈에서 발생했는지 더욱 구체적으로 구분할 수 있고, 런치 파일이나 환경 변수에서 로거 이름을 별도로 지정해 세부 로그 레벨을 달리 설정하는 등 유연한 로깅 체계를 구성할 수 있다.

#### 로깅 출력 채널의 선택

ROS2 내에서 로깅은 주로 표준 출력(콘솔) 또는 로그 파일을 통해 확인하지만, 경우에 따라 Syslog나 기타 외부 로깅 시스템으로 전송을 원할 수 있다. ROS2의 로깅 레이어는 기본적으로 `rcutils`의 로깅 매커니즘을 사용하며, 필요하다면 커스텀 로깅 핸들러를 구현해 원하는 채널로 로그를 송출할 수 있다. 예를 들어, 사내 시스템에서 제공하는 중앙 집중식 로깅 서버로 전송하려면 `rcutils_logging_initialize`를 통해 별도의 콜백 함수를 등록하고, 해당 콜백에서 로그를 받아 원하는 곳에 전송하는 식이다.

다만, 이는 일반적인 ROS2 사용 범위 바깥의 고급 활용에 해당하므로, ROS2 런치 시스템 내에서의 설정만으로는 완전한 커스텀 로깅 채널을 구성하기 어렵다. 대체로 다음과 같은 단계를 수행하게 된다.

1. 커스텀 로깅 핸들러 구현 (C/C++ 레벨)
2. 런치 파일로 노드 실행 시, 해당 로깅 핸들러가 동작하도록 환경 변수나 초기화 코드를 통해 설정

#### 로그 필터링과 모듈별 레벨 설정

일부 로거만 INFO를 출력하고, 나머지 로거는 WARN 이상만 출력하게 하고 싶을 때가 있다. ROS2에서는 노드 전역에 레벨을 설정하는 것 외에, 각 로거 이름별로 상이한 로깅 레벨을 설정할 수 있다. 이는 다음과 같은 형식으로 런치 인자에서 표현할 수 있다.

```bash
ros2 run my_package my_node --ros-args \
  --log-level main_logger:=INFO \
  --log-level sensor_data_logger:=WARN \
  --log-level custom_logger:=DEBUG
```

런치 파일에서도 동일한 방식으로 `Node` 액션의 `arguments`에 여러 번 `--log-level` 옵션을 주어 모듈별, 로거별 세분화된 로깅 레벨을 지정할 수 있다.

#### mermaid를 이용한 로거 구조 개념도

로깅 호출이 어떤 과정을 거쳐 콘솔 혹은 파일로 전달되는지 간단한 구조를 mermaid로 표현하면 아래와 같다:

{% @mermaid/diagram content="flowchart LR
A\["A: 노드 코드 (rclcpp/rclpy)"] --> B\[B: Logger API 호출]
B --> C\["C: ROS2 Logging Layer (rcutils)"]
C -->|표준 출력| D((D: 콘솔))
C -->|파일 출력| E((E: 로그 \n 파일))
C -->|옵션에 따라| F((F: 커스텀 \n핸들러))" %}

* **A**: 사용자 노드 코드 (C++/Python)
* **B**: `RCLCPP_*, rclpy.get_logger().*`를 통한 로깅 API 호출
* **C**: rcutils 로깅 레이어에서 환경 변수, 런치 인자 등을 해석하여 로그 레벨 및 출력 대상 결정
* **D**: 콘솔 출력 (`output="screen"`)
* **E**: 파일 출력 (`output="log"`)
* **F**: 별도 커스텀 핸들러(예: 중앙 로깅 서버 전송)

#### 로그 출력 제한(Throttle) 및 1회성(Once) 매크로

ROS2(C++)에서 자주 사용하는 로깅 매크로로는 `RCLCPP_INFO`, `RCLCPP_WARN` 등이 있다. 그런데 로그가 너무 자주 발생하면 성능에 부담이 되거나, 로그가 과도하게 쌓여 분석이 어려울 수 있다. 이를 방지하기 위해 ROS2는 다음과 같은 매크로를 제공한다.

1. **Once 매크로**
   * 예시: `RCLCPP_INFO_ONCE(node->get_logger(), "메시지")`
   * 노드 실행 중 해당 메시지를 단 한 번만 출력한다. 조건이 충족될 때마다 매번 로그가 찍히는 상황을 피하고 싶을 때 사용한다.
2. **Throttle 매크로**
   * 예시: `RCLCPP_INFO_THROTTLE(node->get_logger(), node->get_clock(), 2000, "메시지")[계속]`
   * 두 번째 인자로 `rclcpp::Clock`(또는 node->get\_clock())를, 세 번째 인자로 출력 주기(단위: ms)를 지정한다. 지정한 시간 간격 내에는 로그가 반복 출력되지 않는다.
   * 예를 들어 위 예시는 2초(2000ms)마다 한 번씩만 로그가 찍히도록 제한한다. 높은 주기로 콜백이 호출되어도, 2초에 한 번만 같은 로그 메시지가 출력된다.

> **주의:** Throttle 매크로 사용 시, 사용하는 `Clock` 종류에 따라 시스템 시간 또는 ROS 시간(시뮬레이션, 백테스팅 등에서 사용)이 적용될 수 있다. 일반적으로 `node->get_clock()`는 ROS 시간을 기본으로 취급하므로, 시뮬레이션 중에는 시뮬레이션 시간에 기반한 스로틀이 걸린다.

#### 성능과 로깅 레벨의 관계

로깅 레벨 설정은 단순히 모니터링을 위해서뿐 아니라, 노드의 성능 및 CPU 사용량과도 밀접한 관련이 있다. 예컨대 높은 빈도로 호출되는 콜백 내부에서 DEBUG 메시지를 자주 호출하면, 출력 지연과 문자열 포매팅 비용이 누적되어 시스템 성능이 떨어질 수 있다. 따라서 다음 사항을 고려한다.

* **개발/디버그 모드**: DEBUG 혹은 INFO 레벨로 세부 정보를 풍부하게 남긴다.
* **테스트/운영 모드**: 핵심 문제(에러, 경고)에 집중하도록 WARN 이상의 레벨만 출력한다.
* **자주 호출되는 영역**: Throttle 매크로 또는 Once 매크로를 적절히 활용해 로그 폭주를 방지한다.

#### 런치 파일에서 Throttle 매크로와의 연동

Throttle 매크로나 Once 매크로는 코드 레벨에서 동작하는 것으로, 런치 파일에서 이를 직접 제어하지는 않는다. 하지만 런치 파일로 노드 로그 레벨을 제한하면 Throttle 매크로 또한 그 레벨 이상의 메시지를 대상으로만 동작하게 된다. 예를 들어 런치 파일에서 `--log-level WARN`으로 설정하면, `RCLCPP_INFO_THROTTLE`가 아무리 많아도 WARN 레벨 이상 로그만 실질적으로 표시된다.

#### 로그 파일 압축, 회전(Rotation) 등

ROS2 기본 기능만으로 로그 압축이나 자동 회전(rolling)을 지원하지는 않는다. 로그 파일이 커지면 별도의 스크립트나 시스템 도구(logrotate 등)를 이용하여 주기적으로 압축 및 백업하는 방식을 사용해야 한다. Linux 환경에서 예시를 들면 다음과 같은 `logrotate` 설정 파일을 작성할 수 있다.

```bash
# /etc/logrotate.d/ros2_logs
/home/my_user/.ros/log/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 644 my_user my_user
}
```

위 설정은 매일 `.log`로 끝나는 파일을 찾아서 새 로그 파일로 회전시키고, 7번 회전 후 로그를 삭제하며, 압축을 수행하는 일반적인 logrotate 설정 예시다. ROS2 런치 파일 레벨에서 제어할 수 있는 사항은 아니지만, 시스템 관리자 입장에서는 이러한 방식으로 로그 관리를 자동화할 수 있다.

#### ROS2 런치 콘솔 출력 색상

ROS2 런치 실행 시, 콘솔에서 레벨별 색상이 적용되어 메시지 가독성이 높아진다. 보통 다음과 같은 색상 체계를 갖는다.

* **DEBUG**: 회색 혹은 파란색
* **INFO**: 흰색 혹은 녹색
* **WARN**: 노란색
* **ERROR**: 빨간색
* **FATAL**: 진한 빨간색 또는 깜박임

이는 일반적으로 `rcutils`가 ANSI 컬러 코드를 사용해 구현한 것이므로, 특정 터미널 환경이나 OS에서 다르게 표시될 수도 있다. 색상 출력을 끄고 싶다면 `$RCUTILS_COLORIZE_OUTPUT` 환경 변수를 `0`으로 설정하면 된다.

```bash
export RCUTILS_COLORIZE_OUTPUT=0
```

이렇게 설정하면 터미널에 색상 없이 단순 텍스트로만 로그 메시지를 확인할 수 있다.

#### 에러 메시지 포맷팅 및 스택 트레이스

ROS2 코어 로깅 시스템은 에러 로그에 대한 스택 트레이스(함수 호출 경로)를 자동으로 출력해 주지 않는다. 따라서 에러 메시지에 호출 경로나 추가 정보를 붙이고 싶다면 직접 포맷팅하여 로깅하면 된다.

```cpp
RCLCPP_ERROR(
  node->get_logger(),
  "Error in function %s, line %d: %s",
  __FUNCTION__,
  __LINE__,
  "Some error occurred"
);
```

이처럼 C++ 매크로를 이용해 함수명, 라인 번호 등을 함께 출력하면, 디버깅 시 보다 빠르게 문제 발생 위치를 확인할 수 있다.

#### 런치 시스템에서의 다중 노드 로깅 관리

ROS2 런치 파일은 여러 노드를 동시에 실행할 수 있다. 이때 각 노드별 로깅 레벨과 출력 방식을 달리 설정할 수 있다는 점이 중요하다. 예를 들어 센서 데이터를 처리하는 노드는 `DEBUG` 레벨로 상세 로그를 남기고, 임베디드 하드웨어를 제어하는 노드는 `ERROR` 수준만 출력하도록 할 수 있다.

아래는 간단한 예시 런치 파일이다.

```python
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_sensors',
            executable='sensor_node',
            name='sensor_node',
            output='screen',
            arguments=['--ros-args', '--log-level', 'DEBUG']
        ),
        Node(
            package='my_controller',
            executable='controller_node',
            name='controller_node',
            output='log',
            arguments=['--ros-args', '--log-level', 'ERROR']
        )
    ])
```

* `sensor_node`: 콘솔에 직접 DEBUG 이상의 모든 메시지를 출력
* `controller_node`: 로그 파일에만 ERROR 이상의 메시지를 남김

실제 운영 환경에서는 CPU 사용률이나 디스크 용량, 네트워크 트래픽 등을 고려하여 이러한 로깅 전략을 세우게 된다.

#### 런치 파일에서 로깅 관련 디렉티브(Directive) 활용

ROS2 런치 시스템은 `IncludeLaunchDescription`, `GroupAction` 등 다양한 디렉티브를 제공한다. 로깅만 따로 묶는 용도로 `GroupAction`을 쓸 수도 있고, 별도 런치 파일에서 로깅 옵션만 모아두고 `IncludeLaunchDescription`으로 가져오는 식으로 구성할 수도 있다. 예를 들어, 다음처럼 “로깅 설정 전용 런치 파일”을 만들어 놓고, 다른 런치 파일에서 이를 불러올 수 있다.

**logging\_setup.launch.py**:

```python
from launch import LaunchDescription
from launch.actions import SetEnvironmentVariable

def generate_launch_description():
    return LaunchDescription([
        SetEnvironmentVariable('RCL_LOG_LEVEL', 'WARN'),
        SetEnvironmentVariable('RCUTILS_COLORIZE_OUTPUT', '1'),
    ])
```

**main\_launch\_file.launch.py**:

```python
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
import os

def generate_launch_description():
    logging_setup_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(
                get_package_share_directory('my_package'),
                'launch',
                'logging_setup.launch.py'
            )
        )
    )

    # 다른 노드 실행 액션들...
    # Node(...)

    return LaunchDescription([
        logging_setup_launch,
        # 다른 액션들...
    ])
```

이렇게 하면 한 번에 전체 런치 파일에 로깅 환경 변수를 적용하여, 운영 모드 변경 시 이 런치 파일 하나만 수정하면 된다.

#### Docker / 컨테이너 환경에서의 로깅

실제 배포 환경에서 Docker나 컨테이너로 ROS2 시스템을 구성하는 경우도 많다. 이때 로그가 제대로 수집되는지, 혹은 컨테이너 종료와 함께 사라지는지 등을 주의해야 한다.

* **stdout / stderr 로그 수집**: Docker 기본 로깅 드라이버를 사용하면, 컨테이너 내부에서 `stdout`이나 `stderr`로 출력되는 로그를 Docker 데몬이 자동으로 수집한다. 이때 `output='screen'` 옵션으로 노드를 실행하면 대부분의 메시지가 Docker의 `docker logs` 명령으로 확인 가능하다.
* **파일 기록**: 컨테이너 내부 경로(예: `/root/.ros/log` 등)에 로그를 기록하도록 설정하면, 컨테이너를 지우면 로그도 함께 삭제될 수 있다. 이 경우, 호스트와 볼륨 마운트하여 로그 디렉터리를 공유하거나, 외부 로깅 시스템을 이용해야 한다.

#### 로그 분석 도구 활용

ROS2의 로그는 기본적으로 텍스트 파일이므로, 일반적인 로그 분석 도구나 파이썬 스크립트를 통해 처리할 수 있다. 다만 로봇 실시간 이벤트나 오케스트레이션 시스템을 다루려면, Kibana, Splunk 등 중앙 로그 서버와 연동하는 방안도 고려해 볼 수 있다.

1. **grep** / **awk** / **sed**: 간단한 키워드 검색, 패턴 필터링
2. **logrotate**: 로그 파일 자동 분할 및 보관
3. **Kibana/Elasticsearch**: 여러 장비에서 발생하는 로그를 한 곳에 모아 시각화(고급 활용)

#### rcl\_logging 패키지

ROS2에는 `rcl_logging`이라는 패키지가 있으며, rcutils 로깅 레이어를 래핑(wrapping)하고 있다. 이를 확장하거나 대체 구현을 제공해 커스텀 로깅 백엔드(예: syslog, console\_bridge 등)를 사용할 수 있다.

* **rcl\_logging\_spdlog**: `spdlog` 라이브러리를 사용해 포맷팅이나 로그 회전 등 다양한 기능을 지원
* **rcl\_logging\_noop**: 아무 로그도 남기지 않는 백엔드 (성능 테스트나 특수 용도)

런치 파일에서 직접 `rcl_logging` 백엔드를 지정하는 옵션은 제공하지 않지만, 환경 변수나 CMake 설정 등을 통해 빌드 시 결정할 수 있다.

```bash
export RCL_LOGGING_IMPLEMENTATION=spdlog
```

위와 같이 설정하면, ROS2가 `spdlog`를 이용하여 로그를 출력하게 된다. 이를 통해 ROS2의 로깅 메시지가 좀 더 풍부한 포맷(예: 날짜/시간, 스레드ID, 파일명, 함수명)으로 기록될 수 있다.
