Ruby 로깅의 현황과 한계
Ruby의 Logger 표준 라이브러리는 Logger 객체를 통해 로그를 기록하며, 로그 장치(log device)와 포맷터(formatter)를 설정할 수 있습니다. 하지만 기본 포맷터는 다소 독특하며, Syslog 표준에 부합하지 않는 부분이 있습니다. 특히, 로그 장치 인터페이스는 stdout/stderr나 파일 쓰기에는 용이하나, 소켓과 같은 다른 장치에 쓰거나 여러 위치에 동시에 쓰는 기능이 부족합니다.
구조화된 로깅과 Logger::Formatter의 문제
2025년 현재, JSON 형태의 구조화된 로깅은 업계 표준으로 자리 잡고 있습니다. 하지만 Ruby의 Logger::Formatter API는 (severity, time, progname, msg)라는 고정된 인자만을 받도록 설계되어 있어, 메시지 외의 추가 메타데이터(예: 사용자 데이터)를 유연하게 포함하기 어렵습니다. 이로 인해 log4r, lograge, semantic_logger 등 대부분의 구조화된 로깅 라이브러리는 표준 Logger를 기반으로 하지 않고 자체적인 포맷터 API를 재구현하는 비효율을 초래합니다.
Rails 로깅의 특성과 한계
Rails 애플리케이션은 Rails.logger를 통해 로깅하며, 이는 ActiveSupport::BroadcastLogger 인스턴스로 여러 로그 장치에 메시지를 전달할 수 있습니다. ActiveSupport::TaggedLogging은 특정 스코프에 컨텍스트 태그를 추가하는 기능을 제공하지만, 여전히 구조화된 로깅, 특히 JSON 포맷에 필요한 유연한 메타데이터 추가에는 한계가 있습니다. lograge와 semantic_logger 같은 라이브러리들이 이러한 문제를 해결하려 하지만, 각각 특정 이벤트에만 적용되거나 API가 다소 복잡하다는 단점이 있습니다.
새로운 컨텍스트 API 제안
이러한 문제들을 해결하기 위해, 저자는 Ruby Logger 표준 라이브러리에 컨텍스트 API를 제안합니다. 주요 기능은 다음과 같습니다:
- **블록 기반 컨텍스트 (`Logger
with_context)**: 특정 코드 블록 내의 모든 로그 메시지에 컨텍스트를 추가하며, 블록 종료 시 컨텍스트가 자동으로 해제됩니다. 중첩된 with_context` 호출도 지원합니다.
- **호출 기반 컨텍스트 (`Logger
info(context:))**: info, error` 등 개별 로그 호출에 직접 컨텍스트를 키워드 인자로 전달할 수 있습니다.
```ruby # 블록 기반 컨텍스트 예시 logger.with_context(a: 1) do logger.info(“foo”) # => I, [a=1] … foo end
호출 기반 컨텍스트 예시
logger.info(“foo”, context: {user_id: 1}) # => I, [user_id=1] … foo ```
다양한 환경에서의 컨텍스트 활용
이 API는 Rack 미들웨어, Rails around_context 헬퍼, Sidekiq 및 Active Job의 미들웨어/around_perform 콜백 등을 통해 요청/작업 라이프사이클 전반에 걸쳐 컨텍스트를 자동으로 관리할 수 있게 합니다. 이는 customer_id, request_id, job_id와 같은 중요한 정보를 모든 관련 로그에 투명하게 포함할 수 있게 합니다.
다른 언어의 컨텍스트 로깅
-
Java (log4j):
ThreadContext를 통해 스레드별 컨텍스트를 관리하며,CloseableThreadContext로 자동 해제를 지원합니다. -
Go (slog):
context.Context객체를 사용하여 구조화된 컨텍스트를 전달하거나,Logger인스턴스 자체를.With호출로 확장하여 컨텍스트를 추가합니다. -
Python (logging):
extra키워드 인자를 통해 호출별 컨텍스트를 지원하며,LoggerAdapter나logging.Filter를 통해 프레임워크별 컨텍스트 저장소와 연동할 수 있으나, 다소 장황하고 비인체공학적이라는 평가를 받습니다.
Rails 8의 새로운 이벤트 구독 API
Rails 8의 “Structured Event Reporting”은 이벤트에 컨텍스트를 전달하는 문제를 부분적으로 해결하지만, Rails.logger를 통한 일반적인 애플리케이션 로깅 컨텍스트 문제 해결에는 직접적인 기여가 제한적입니다.