# ROS2 Humble 로깅 시스템 이해

#### 로깅 시스템의 개념

ROS2에서 로깅(logging)은 노드가 동작하면서 남기는 각종 메시지, 상태, 경고 등을 기록하고 관리하기 위한 핵심 기능이다. 로깅을 통해 시스템 장애 발생 시점과 원인을 추적하거나, 특정 알고리즘의 중간 단계를 시각화하는 등의 다양한 활용이 가능하다. 특히 ROS2 Humble에서는 기존 ROS2 버전들과 달리, 보다 유연한 설정과 다양한 출력 방식을 제공하기 때문에, 시스템 규모가 커져도 일관된 로깅 정책을 유지할 수 있다.

일반적으로 ROS2의 로깅 메시지는 다음과 같은 흐름을 거친다.

{% @mermaid/diagram content="flowchart LR
A\[Logger 호출] --> B\[Logger API]
B --> C\[rclcpp/rclpy 내부 로거]
C --> D\[Handler: Console/File/Custom]" %}

* **Logger 호출**: 개발자가 노드 코드에서 로그 매크로나 함수(rclcpp/rclpy)를 통해 로그 메시지를 요청한다.
* **Logger API**: ROS2의 로깅 메커니즘은 `rclcpp::Logger` 또는 `rclpy.logging.Logger` 등을 사용한다.
* **내부 로거(rclcpp/rclpy)**: 실제로 로그 메시지를 처리할 때, ROS2의 핵심 라이브러리 계층(rcl, rcutils)로 전달되어 적절한 로그 포맷과 수준(Filter) 등이 적용된다.
* **핸들러(Handler)**: 마지막으로 로그 메시지는 콘솔(stdout), 파일, 혹은 커스텀 핸들러 등 사용자가 선택한 출력 경로로 기록된다.

#### 로깅의 기본 구조와 레이어

ROS2의 로깅은 크게 다음의 여러 계층(layer)로 구성된다.

1. **사용자 코드 레벨**
   * C++의 경우 `rclcpp::Logger` 객체를 통해 매크로(`RCLCPP_INFO`, `RCLCPP_WARN`, `RCLCPP_ERROR` 등)를 사용한다.
   * Python의 경우 `rclpy.logging` 모듈의 `get_logger()`로 로거를 생성하고 `info()`, `warn()`, `error()` 등의 함수를 사용한다.
2. **RCL 계층**
   * ROS2의 핵심 라이브러리 중 하나인 RCL에서 로깅 레벨을 설정하거나, 로깅 메시지의 포맷 등을 정의한다.
   * 필요에 따라 로깅 콜백 함수를 등록해, 사용자 정의 형식으로 로그 메시지를 가공할 수도 있다.
3. **RCUTILS 계층**
   * ROS2 Humble의 로깅 시스템은 `rcutils/logging.h` 인터페이스를 통해 가장 기초적인 로그 기록의 로직을 수행한다.
   * 로깅을 위한 시간 정보, 문자열 포맷, 스레드 안전성 등이 이 계층에서 관리된다.
4. **Output Sink**
   * 로그 메시지가 최종적으로 어디에 기록되는지 결정된다.
   * 기본적으로 콘솔(stdout)에 출력되지만, 환경 변수나 파라미터 설정을 통해 파일 출력, CSV, 원격 로깅 서버 등으로 확장 가능하다.

#### 로그 레벨과 메시지 필터링

ROS2 로깅 시스템에서 가장 중요하게 다루는 요소 중 하나가 \*\*로그 레벨(Log Level)\*\*이다. 대표적으로 다음의 로깅 레벨을 지원한다.

* DEBUG
* INFO
* WARN
* ERROR
* FATAL

코드 내에서 INFO 수준으로 로그를 남기더라도, 실제로 INFO 이상의 로그만 출력하도록 설정하면 INFO, WARN, ERROR, FATAL 메시지만 보이고 DEBUG는 무시된다. 이처럼 ROS2에서 로그 레벨은 다음과 같은 규칙을 따라 필터링된다.

$$
\text{출력 대상 레벨} \le \text{현재 설정된 최소 레벨}
$$

예를 들어, 현재 설정이 WARN이라면, WARN, ERROR, FATAL 수준의 메시지만 출력된다. 따라서 디버깅 중인 경우 로그 레벨을 DEBUG나 INFO로 두고, 안정화 단계에서는 WARN이나 ERROR로 조정함으로써, 필요 이상의 메시지로 로그가 과도하게 쌓이는 것을 방지할 수 있다.

#### 로깅 설정과 환경 변수

ROS2 Humble의 로깅 시스템을 보다 유연하게 활용하기 위해서는 여러 설정 방법을 이해해야 한다. 단순히 코드 상의 로그 레벨을 지정하는 것 외에도, 다양한 환경 변수를 활용하여 런타임 시점에 로깅 형태를 변경할 수 있다.

**ROS\_LOG\_DIR**:

* 로그 파일을 저장할 디렉터리를 지정한다.
* 다음과 같은 방식으로 설정할 수 있다.

```bash
export ROS_LOG_DIR=/path/to/log/directory
```

* 설정 후, ROS2 노드 실행 시 로그 파일이 해당 디렉터리에 생성된다.

**RCUTILS\_CONSOLE\_OUTPUT\_FORMAT**:

* 콘솔에 출력되는 로그 메시지의 포맷을 정의한다.
* 예를 들어, 로그 레벨, 시간, 노드 이름 등을 커스텀 포맷으로 조합하려면 아래처럼 설정하면 된다.

```bash
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity}] [{name}]: {message}"
```

* 가능한 포맷 플레이스홀더(placeholder)는 `{severity}`, `{name}`, `{function}`, `{line}`, `{message}`, `{time}`, `{thread_id}` 등이 있다.

**RCUTILS\_CONSOLE\_STDOUT\_LINE\_BUFFERED**:

* 표준 출력(stdout)에 출력되는 로그 메시지를 라인(Line) 단위로 버퍼링할지 여부를 결정한다.
* `1`로 설정하면 각 로그가 개행 문자가 출력된 후에야 실제로 출력된다.

```bash
export RCUTILS_CONSOLE_STDOUT_LINE_BUFFERED=1
```

* 일반적으로 라인 단위 버퍼링은 디버깅 시 로그가 즉시 보이도록 하는 데 유용하다.

**RCUTILS\_LOGGING\_SEVERITY\_THRESHOLD**:

* 현재 프로세스에서 출력할 최소 로그 레벨을 설정한다.
* 예를 들어, 아래와 같이 설정하면 WARN 이상의 로그만 출력된다.

```bash
export RCUTILS_LOGGING_SEVERITY_THRESHOLD=WARN
```

* 내부적으로 `DEBUG=10`, `INFO=20`, `WARN=30`, `ERROR=40`, `FATAL=50` 정도의 우선순위를 가지므로, WARN으로 설정하면 30 이상 레벨의 로그만 출력된다.

#### 파일 로깅과 버퍼링

로깅을 파일로 기록할 때는 주로 `ROS_LOG_DIR` 환경 변수를 사용하거나, 노드 실행 시 별도의 옵션을 제공해 지정한다. 이때, 로그 파일은 노드 이름, 타임스탬프, 프로세스 ID 등이 붙은 형태로 자동 생성되어, 여러 노드의 로그가 서로 섞이지 않도록 관리한다.

버퍼링(Buffering)은 로깅 시스템이 메시지를 즉시 쓰지 않고, 일정량을 모았다가 한 번에 쓰는 동작을 말한다.

* **장점**: 디스크 접근 횟수가 줄어 I/O 부하가 낮아진다.
* **단점**: 충돌이나 종료 시점에 일부 로그가 유실될 가능성이 있다(특히 OS나 다른 애플리케이션 에러로 비정상 종료된 경우).

ROS2의 기본 로깅 구현은 stdout 출력에 대해서는 라인 버퍼링을 적용할 수 있으나, 파일 출력에 대해서는 일반적으로 표준 C 라이브러리의 버퍼링 방식을 따른다. 따라서 크리티컬한 운영 환경에서는 별도의 플러시(flush) 정책이나 로그 파일 동기화(Sync) 메커니즘을 고려해야 한다.

#### rclcpp와 rclpy에서의 로거 사용

ROS2 노드를 구현할 때, 각 언어(C++ 또는 Python)별로 로거를 어떻게 사용하는지 이해하는 것은 중요하다.

**C++ (rclcpp)**:

* 노드 상속 클래스를 정의할 때, 보통 `rclcpp::Node`를 상속받아서 노드를 구현한다.
* 매크로를 사용한 로그 예시:

```cpp
#include <rclcpp/rclcpp.hpp>

class MyNode : public rclcpp::Node
{
public:
  MyNode()
  : Node("my_node")
  {
    RCLCPP_INFO(this->get_logger(), "Node initialized");
    RCLCPP_WARN(this->get_logger(), "Warning: something might be wrong");
    RCLCPP_ERROR(this->get_logger(), "Error encountered!");
  }
};

int main(int argc, char** argv)
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MyNode>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
```

* `RCLCPP_XXX` 형태의 매크로(`INFO`, `WARN`, `ERROR` 등)를 사용하여, `this->get_logger()`로부터 얻은 로거를 인자로 넘긴다.
* 매크로와 함께 포맷 문자열을 사용하는 예:

```cpp
RCLCPP_INFO(this->get_logger(), "Current value: %d", current_value);
```

**Python (rclpy)**:

* Python 노드에서도 유사하게 로깅 기능을 사용할 수 있다.
* 대표적으로 `rclpy.logging` 모듈의 `get_logger()` 함수를 이용한다:

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

  class MyPyNode(Node):
      def __init__(self):
          super().__init__('my_python_node')
          self.get_logger().info("Node initialized")
          self.get_logger().warn("Warning: something might be wrong")
          self.get_logger().error("Error encountered!")

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

  if __name__ == '__main__':
      main()
  ```
* `self.get_logger()`로 로거 객체를 얻고, `.info()`, `.warn()`, `.error()` 등의 함수를 호출한다.
* 포맷 문자열이나 f-string과 함께 쓰는 예:

```python
self.get_logger().info(f"Current value: {current_value}")
```

이처럼 C++의 `RCLCPP_INFO` 계열 매크로와 Python의 `.info()` 계열 메서드 모두, 내부적으로 ROS2의 로깅 시스템을 거쳐 동일한 방식으로 처리된다. 다만, 파라미터로 넘기는 인자 형식이나 문자열 포맷 방식에서 차이가 있다.

#### 동적 로그 레벨 조정

런타임 도중에(프로그램 실행 중) 로그 레벨을 바꿔야 할 때가 종종 있다. 예를 들어, 배포 단계에서 WARN 이상의 로그만 보고 있다가, 특정 이벤트 발생 시점에서 DEBUG 로그를 잠시 켜보고 싶을 수 있다.

ROS2 Humble부터는 `rclcpp::Logger::set_level()` 또는 `rclpy.logging` 모듈의 API를 사용하여 동적으로 로깅 레벨을 변경할 수 있다.

C++ 예시:

```cpp
auto logger = rclcpp::get_logger("my_dynamic_logger");
logger.set_level(rclcpp::Logger::Level::Debug);
RCLCPP_DEBUG(logger, "This debug log is now visible");
```

Python 예시:

```python
import rclpy
from rclpy.logging import LoggingSeverity

logger = rclpy.logging.get_logger("my_dynamic_logger")
logger.set_level(LoggingSeverity.DEBUG)
logger.debug("This debug log is now visible")
```

동적으로 로그 레벨을 바꿀 때 주의할 점은, 각 노드에 대한 별도의 로거가 존재하므로, 레벨을 변경해야 하는 대상 로거를 정확하게 지정해야 한다는 것이다. 만약 전체 노드나 전체 시스템의 로깅 레벨을 바꾸고 싶다면, 모든 로거 혹은 기본 로거에 대해 일괄적으로 레벨을 조정해야 한다.

#### 커스텀 로거(Custom Logger) 확장

ROS2의 로깅 시스템은 이미 다양한 출력 방식을 제공하지만, 프로젝트 규모가 커지거나 특정 요구 사항(예: 네트워크 전송, 데이터베이스 기록 등)이 있을 경우, 사용자 정의 로거를 직접 구현해야 할 때가 있다. 이를 위해 ROS2에서는 **RCUTILS** 계층에 로거 콜백을 등록하거나, 자체적으로 **Sink**(출력 핸들러)를 만들어 확장하는 방식을 지원한다.

**RCUTILS를 통한 커스텀 로거 등록**

* ROS2의 가장 기초적인 로깅 API는 `rcutils/logging.h`에 정의되어 있으며, 여기서 사용자가 직접 콜백 함수를 등록할 수 있다.
* 예시:

```cpp
#include <rcutils/logging.h>

void my_custom_logger(
  const rcutils_log_location_t* location,
  int severity,
  const char* name,
  const char* message)
{
  // 원하는 방식으로 로그를 처리한다. (예: 외부 DB 저장, 네트워크 전송 등)
  // location 파라미터에는 함수명, 파일명, 라인 번호 등이 담겨 있을 수 있다.
}

int main(int argc, char** argv)
{
  // 기존 ROS2 초기화
  // ...

  // 커스텀 로거 등록
  rcutils_logging_set_logger(my_custom_logger);
  // 이제부터 모든 로깅 호출은 my_custom_logger를 통해 전달됨

  return 0;
}
```

* C++나 Python 노드에서 발생하는 모든 로그가 결국 `rcutils_logging_set_logger()`로 등록된 함수를 타게 되므로, 로깅을 가공하거나, 추가 처리 로직을 삽입하기에 적합하다.

**커스텀 Sink(출력 핸들러) 구현**:

* ROS2 내부적으로 콘솔 출력이나 파일 출력 등은 “Sink” 개념을 통해 처리된다.
* 사용자가 별도의 C++ 클래스를 작성하여, `rcutils_logging_set_output_handler()`를 통해 등록하는 방식으로, 메시지를 전달받을 수도 있다.
* 예시(간소화된 형태):

```cpp
#include <rcutils/logging.h>

rcutils_ret_t my_sink_handler(
  rcutils_logging_location_t location,
  int severity,
  const char* name,
  const char* timestamp,
  const char* message)
{
  // 예: 데이터베이스 저장 코드 호출
  // ...
  return RCUTILS_RET_OK;
}

int main(int argc, char** argv)
{
  // ROS2 초기화
  // ...

  // Sink Handler 등록
  rcutils_logging_set_output_handler(my_sink_handler);

  // 이후 모든 로그는 my_sink_handler를 통해 전달
  return 0;
}
```

* 이 방법은 시스템 전반의 로그 출력을 한 곳으로 모으고, 그 결과를 여러 경로로 분기(멀티캐스팅)하는 등 고급 기능을 구현할 때 유용하다.

**로거 이름 충돌에 대한 주의**:

* ROS2에서 각 노드는 고유한 이름의 로거를 갖는다.
* 커스텀 로거 설정 시, 특정 이름에 대해 별도 처리 로직을 적용할 수도 있고, 모든 로거에 대해 일괄 처리할 수도 있다.
* 이름 충돌이 우려되는 상황이라면, `rclcpp::Logger::get_name()` 같은 API를 통해 로거 이름을 확인하고, 필요에 따라 구분해주어야 한다.

#### /rosout 토픽과 통합

ROS2에서는 `/rosout`이라는 이름의 토픽에 노드의 로그 메시지가 전달되며, 이를 다른 노드가 구독(subscribe)하여 모니터링할 수 있다. 이는 디버깅 시에 매우 유용하며, 단순히 “프로그램 콘솔” 수준을 넘어 ROS 생태계 내에서 로그를 공유하는 데 핵심 역할을 한다.

**ros2 topic echo /rosout**:

아래와 같이 `/rosout` 명령어로 모니터링할 수 있다.

```bash
ros2 topic echo /rosout
```

그러면 연결된 모든 노드들의 로그 메시지가 해당 토픽을 통해 전달되는 것을 확인할 수 있다.

**LoggingSeverity**:

* `/rosout` 메시지 내부에도 로그 레벨 정보를 포함하고 있으며, 이를 통해 특정 레벨 이상의 메시지만 필터링해서 볼 수도 있다.
* 별도의 노드를 만들어 `/rosout`에 들어오는 메시지 중, 예컨대 `ERROR` 이상의 로그만 다른 토픽으로 재전송할 수도 있다.

**장점**:

* 분산된 로봇 시스템에서 중앙 집중형 로그 수집기를 만들기 쉽다.
* 멀리 떨어진 여러 센서나 장치에서 발생하는 로그를 `/rosout`을 통해 모아서 관리할 수 있다.

**주의사항**:

* `/rosout`에 너무 많은 로그를 보내면 네트워크 트래픽이 불필요하게 커질 수 있다.
* 따라서 실제 운영 환경에서는 DEBUG 레벨 로깅을 제한하거나, 특정 노드의 로그만 구독하는 식으로 조정할 필요가 있다.

#### 로깅 성능과 모범 사례

ROS2 기반의 애플리케이션은 실시간 혹은 준실시간성을 요구하는 경우가 많다. 따라서 로깅 시스템이 과도한 부하를 일으키지 않도록 주의해야 한다. 로깅이 많아지면 그만큼 CPU 및 I/O 리소스를 사용하게 되어, 실제 로봇 동작에 지장을 줄 수 있기 때문이다. 아래는 로깅 성능을 높이기 위한 몇 가지 모범 사례다.

**필요한 로깅 수준만 유지**:

* 디버깅 중이 아니라면, DEBUG 레벨 로깅을 너무 많이 남기지 않는 것이 좋다.
* 과도하게 세밀한 로그(예: 매 반복주기마다 수백\~수천 줄)가 쌓이면 디스크나 네트워크 대역폭에 부담이 된다.

**로그 문자열 생성 비용 최소화**:

C++ 코드에서 문자열 결합이 많은 경우, 실제 로그 출력을 하지 않아도 문자열 생성 비용이 들어갈 수 있다.

예를 들어, 아래 코드를 보면:

```cpp
int a = 10, b = 20;
RCLCPP_DEBUG(this->get_logger(), "Values: a = %d, b = %d", a, b);
```

로그가 비활성화되어도(로그 레벨이 INFO 이상이라 DEBUG가 찍히지 않는 상황이라도), 매크로 내부에서 문자열 포맷팅 연산이 일어날 수 있다.

이를 방지하기 위해 “lazy evaluation” 매크로나, 분기문을 통해 레벨 검사 후에만 문자열을 합성하도록 만들기도 한다.

```cpp
if (this->get_logger().get_effective_level() <= rclcpp::Logger::Level::Debug) {
  RCLCPP_DEBUG(this->get_logger(), "Values: a = %d, b = %d", a, b);
}
```

이렇게 하면 실제로 DEBUG 로그가 활성화되었을 때만 문자열이 만들어진다.

**비동기 로깅(Asynchronous Logging) 고려**:

* 로그를 비동기로 기록하면, 로깅 호출 자체는 빠르게 리턴하고, 별도의 스레드가 실제 I/O를 처리한다.
* 일부 고급 로깅 라이브러리(spdlog, log4cxx 등)와 연동 시, 비동기 모드를 선택할 수 있다.
* 단, 비동기 로깅은 시스템 장애 시 마지막 로그가 유실될 가능성도 있으므로, 신뢰도가 중요한 상황에서는 주의가 필요하다.

**주기적인 Flush와 롤링(Rolling) 파일**:

* 파일 출력을 할 때는 **Flush 주기**와 **롤링(rolling) 정책**도 중요한 요소이다.
* 예: 일정 크기(예: 10MB)마다 새 파일로 로그를 저장하거나, 일정 기간(하루 등)마다 로그 파일을 분할하면, 디스크 관리와 추적이 수월해진다.
* ROS2 자체만으로는 이러한 고급 정책을 모두 제공하지 않으므로, OS 레벨 스크립트나 타 로깅 라이브러리와 병행해 구현하는 경우가 많다.

**멀티스레드 환경에서의 충돌 방지**:

* ROS2 노드는 멀티스레드 스피닝(spinning) 등의 기법을 사용한다.
* 동시에 여러 스레드에서 로깅을 호출해도 안전하도록, ROS2의 로깅 API는 내부적으로 스레드 세이프(thread-safe)를 보장한다.
* 그렇지만, 커스텀 로거를 구현하거나 외부 라이브러리를 연동할 때에는 스레드 안전성을 별도로 고려해야 한다.

**ROS2 파라미터 서버와 연동**:

* 로그 레벨을 “파라미터” 형태로 등록해 두면, ROS2 파라미터 동적 재설정을 통해 런타임 중에도 손쉽게 변경 가능하다.
* 예: `/my_node/log_level` 파라미터를 업데이트하면 내부에서 `logger.set_level()`을 호출하게 구성할 수 있다.

#### ros2doctor와 로깅 분석

ROS2는 시스템 상태를 점검하기 위한 `ros2 doctor` 명령어를 제공한다. 비록 주로 환경 설정과 호환성 이슈 진단에 초점이 맞춰져 있으나, 로깅 설정 관련 부분도 일부 확인할 수 있다.

**명령어 예시**:

```bash
ros2 doctor --report
```

네트워크 상태, 노드 상태 등을 점검하면서 로깅 관련 오류(예: 로그 디렉터리 권한 문제, 환경 변수 충돌 등)도 파악할 수 있다.

**확장 플러그인**:

* `ros2 doctor`는 플러그인 구조이므로, 프로젝트에서 직접 로그 분석 플러그인을 추가할 수도 있다.
* 이를 통해 로그 출력이 과도하게 발생하는 노드를 식별하거나, 특정 시점에 발생한 에러 로그를 요약해서 보여줄 수도 있다.

#### 외부 로깅 라이브러리와의 연동

ROS2는 자체 로거를 사용하지만, 다음과 같이 외부의 유명 로깅 라이브러리와 연동하는 사례도 많다.

* **log4cxx**
  * 기존 ROS(ROS1)에서 주로 사용되던 Java Apache 계열 로깅 시스템이다.
  * ROS2에서는 완벽히 통합되어 있지 않으나, C++ 레벨에서 log4cxx를 직접 사용할 수 있고, ROS2 로그 매크로 대신 log4cxx 매크로를 병행해서 쓸 수도 있다.
* **spdlog**
  * 빠른 C++ 로깅 라이브러리 중 하나로, 비동기 로깅 지원이 강점이다.
  * ROS2 환경에 맞춰 별도의 Adapter를 구현해두면, ROS2 매크로(RCLCPP\_XXX) 호출 대신 spdlog 함수를 호출하도록 바꿀 수 있다.
* **Python 로깅(logging 모듈)**
  * Python에서는 기본적으로 `logging` 모듈이 존재한다.
  * rclpy의 logger와 Python 표준 logging 모듈을 함께 사용하려면, 핸들러나 포맷을 조정해야 할 수도 있다.

이처럼 외부 라이브러리와의 연동 여부는 프로젝트 규모와 요구 사항에 따라 달라진다. ROS2가 제공하는 로깅 기능만으로도 대부분의 상황에서 충분하지만, 수십\~수백 개의 노드를 동시에 운영해야 하거나, 중앙 집중식 모니터링(ELK stack, Grafana 등)을 구축하려는 경우에는 외부 로거와의 연동이 더 유리할 수도 있다.

#### 명령줄 인자(ROS arguments)를 통한 로깅 설정

ROS2 Humble에서는 노드를 실행할 때 \*\*명령줄 인자(ROS arguments)\*\*를 통해 로깅 레벨이나 노드 이름 등 다양한 설정을 동적으로 제어할 수 있다. 이를 적절히 활용하면, 별도의 코드 수정 없이도 필요에 따라 로깅 정책을 달리 적용할 수 있다.

**--ros-args --log-level**:

* 노드 실행 시, 전체 로깅 레벨을 지정한다. 예를 들어, 다음 명령은 DEBUG 레벨 이상의 모든 로그를 출력하도록 설정한다:

```bash
ros2 run my_package my_executable --ros-args --log-level DEBUG
```

* 특정 노드 이름에 대해서만 로그 레벨을 지정하고 싶다면, `--log-level <node_name>:=<LEVEL>` 형태도 사용 가능하다(일부 버전에서 동작).

**--ros-args --remap**:

* 노드 이름을 런타임에 바꿀 때 사용하는 기능이지만, 로깅과 직접적인 연관은 없으나 로거 이름이 노드 이름에 의존할 수 있으므로 간접적인 영향을 미친다.
* 예시:

```bash
ros2 run my_package my_executable --ros-args --remap __node:=my_new_node_name
```

* 이렇게 하면 프로그램 내에서 `this->get_name()` 또는 `self.get_name()`이 반환하는 노드 이름이 `my_new_node_name`이 되고, 로깅 출력을 모니터링할 때도 이 이름이 쓰인다.

**다중 노드 실행 시 고려사항**:

* launch 파일이나 명령줄에서 여러 노드를 동시에 실행할 때, 각 노드별로 로깅 설정을 다르게 줄 수도 있다.
* 예: launch 파일 내에서 `Node` 객체를 생성할 때 `arguments=['--ros-args', '--log-level', 'WARN']` 같은 방식으로 인자를 넘길 수 있다.

#### 색상 출력과 RCUTILS\_COLORIZED\_OUTPUT

ROS2 콘솔 로그는 기본적으로 색상을 사용해 로그 레벨을 구분한다(예: WARN은 노란색, ERROR는 빨간색). 만약 이 기능이 제대로 동작하지 않거나, 특정 환경에서 색상 출력을 제어하고 싶다면 다음과 같은 환경 변수를 사용할 수 있다.

`RCUTILS_COLORIZED_OUTPUT`

* `1`(기본값)로 설정되어 있으면, 가능한 터미널에서 색상 로그가 표시된다. `0`으로 설정하면 컬러 출력이 비활성화된다.

```bash
export RCUTILS_COLORIZED_OUTPUT=0
```

* 원격 터미널이나 로그 파일에서 제어 문자가 섞이는 것이 문제라면, 이 변수를 꺼서 흑백 텍스트로만 로그를 보도록 설정할 수 있다.

#### 다중 로거(Multiple Logger) 활용

하나의 노드에서 여러 로거를 만들어, 각각 다른 이름과 레벨로 관리하는 방법도 있다. 예를 들어, 메인 로직을 위한 로거와, 디버깅만 전담하는 별도 로거를 둘 수 있다.

**C++ 예시**:

```cpp
auto main_logger = this->get_logger();
auto debug_logger = rclcpp::get_logger("my_node.debug");

RCLCPP_INFO(main_logger, "Main logger active");
debug_logger.set_level(rclcpp::Logger::Level::Debug);
RCLCPP_DEBUG(debug_logger, "Debug logger active");
```

`main_logger`는 노드의 기본 로거이지만, `debug_logger`는 "my\_node.debug"라는 이름으로 독립적인 레벨 설정이 가능하다.

**Python 예시**:

```python
main_logger = self.get_logger()
debug_logger = rclpy.logging.get_logger('my_python_node.debug')

main_logger.info("Main logger active")
debug_logger.set_level(LoggingSeverity.DEBUG)
debug_logger.debug("Debug logger active")
```

이렇게 하면, `my_python_node.debug` 로거만 동적으로 DEBUG 레벨로 바꿀 수도 있고, 메인 로거는 INFO 레벨을 유지하는 식의 운용이 가능하다.

#### 런타임 파라미터화를 통한 로깅 레벨 관리

ROS2 파라미터 서버(예: `Node::declare_parameter`, `Node::get_parameter` 등)를 통해, 로깅 레벨을 파라미터로 등록해 두면 **동적 파라미터 업데이트**로 런타임에 로그 레벨을 변경할 수 있다.

**C++ 예시**:

```cpp
this->declare_parameter<int>("log_level", 20);  // INFO level
...
// 콜백 등에서 동적 업데이트가 발생하면
int new_level = this->get_parameter("log_level").as_int();
this->get_logger().set_level(static_cast<rclcpp::Logger::Level>(new_level));
```

`new_level`에 해당하는 값(예: 10=DEBUG, 20=INFO, 30=WARN, 40=ERROR, 50=FATAL)에 따라 로그 레벨이 즉시 바뀐다.

**Python 예시**:

```python
self.declare_parameter("log_level", 20)  # INFO
...
new_level = self.get_parameter("log_level").value
self.get_logger().set_level(new_level)
```

필요 시 파라미터 이벤트 콜백(`add_on_set_parameters_callback`)을 통해 파라미터 변경을 감지하면, 자동으로 로거 레벨을 갱신할 수도 있다.

이 기법을 쓰면, launch 파일이나 `ros2 param set` 명령으로 실행 중인 노드의 로그 레벨을 유연하게 바꿀 수 있어, 디버깅 편의성이 크게 향상된다.

#### 로깅 메시지 포맷 커스터마이징 사례

앞서 `RCUTILS_CONSOLE_OUTPUT_FORMAT` 환경 변수를 통해 콘솔 로그 포맷을 수정할 수 있다고 언급했다. 예시로, 현재 시간이나 노드 이름 등을 더 자세히 찍고 싶다면 다음과 같이 설정할 수 있다.

```bash
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{time}][{name}:{severity}] {message}"
```

* `{time}`: 로그가 발생한 시간(초 단위)
* `{name}`: 로거 이름
* `{severity}`: 로그 레벨(INFO, WARN, 등)
* `{message}`: 실제 로그 메시지

여기서 `{function}`, `{line}`, `{file}` 같은 추가 정보를 함께 표시할 수도 있으나, 성능에 미세한 영향이 있을 수 있다. 따라서 필요에 따라 적절히 설정하는 것이 좋다.

위에서 살펴본 것처럼 ROS2 Humble의 로깅 시스템은 단순히 콘솔에 메시지를 찍는 기능에 그치지 않고, **환경 변수**, **명령줄 인자**, **파라미터 서버**, **커스텀 로거** 등 다양한 확장성과 유연성을 제공한다. 프로젝트 규모와 요구 사항에 맞춰 이러한 기능들을 조합해서 사용하면, 디버깅과 운영 모두에 유리한 로깅 체계를 구축할 수 있다.
