Rails 로그 표준화 전략
1. 표준 로그 포맷의 필요성
애플리케이션이 복잡해짐에 따라 다양한 서비스 클래스와 퍼블리셔에서 생성되는 로그가 서로 다른 형식을 가지게 되어 가독성이 저하됩니다. 이를 해결하기 위해 다음과 같은 명확한 키-값(Key-Value) 구조의 포맷을 정의하여 검색과 필터링을 용이하게 합니다:
timestamp={time} level={severity} source_class={class_name} user_id={email} message={log_message} tag=#{tag}
2. 사용자 정의 로그 포매터(Log Formatter) 구현
Rails에서 로그 형식을 제어하려면 ActiveSupport::Logger::SimpleFormatter를 상속받거나, call(severity, time, progname, msg) 메서드에 응답하는 새로운 포매터 클래스를 생성해야 합니다.
- severity: 로그 레벨 (INFO, WARN, ERROR 등)을 나타냅니다.
- time: 로그가 발생한 정확한 시각을 기록합니다.
- progname: 로거를 사용하는 프로그램의 이름을 나타내며, 주로 라이브러리 로그에서 활용됩니다.
- msg: 실제 기록될 로그 메시지 본문입니다.
3. Rails 설정 및 통합
작성한 커스텀 포매터를 Rails 설정 파일(config/application.rb 또는 각 환경별 설정 파일)에 등록합니다. 이때 Rails의 기본 TaggedLogging 래퍼를 유지하여 기존의 태그 기능을 활용할 수 있도록 설정하는 것이 중요합니다. 이는 config.log_formatter = MyCustomFormatter.new와 같은 방식으로 적용됩니다.
4. 커스텀 속성(source_class, user_id) 추가 전략
로그를 발생시킨 주체나 사용자 정보를 자동으로 추적하기 위해 두 가지 접근 방식을 고려할 수 있습니다.
- 자동 감지 방식: Ruby의 Kernel#caller나 binding_of_caller Gem을 사용하여 호출 스택에서 클래스명을 추출할 수 있으나, 성능 저하와 운영 환경의 안정성 문제로 인해 권장되지 않습니다.
- 명시적 해시 전달 방식: Ruby의 동적 타이핑 특성을 활용하여 logger.info 호출 시 문자열 대신 해시(Hash)를 인자로 전달합니다. 예: logger.info(message: 'Success', source_class: self.class.name). 이 방식은 유연하고 안전합니다.
5. TaggedLogging 및 해시 메시지 처리 최적화
ActiveSupport::TaggedLogging 모듈은 기본적으로 모든 메시지를 문자열로 강제 변환하여 태그와 결합하는 특성이 있습니다. 따라서 포매터 내부에서 입력된 msg가 해시인지 확인하는 로직을 추가해야 합니다. 해시일 경우 각 키와 값을 순회하며 정의된 표준 포맷 문자열로 변환하고, 문자열일 경우 기본 메시지 필드에 할당하여 로그의 일관성을 유지합니다.
6. 최종 구현 결과의 이점
이러한 표준화를 통해 다음과 같은 결과를 얻을 수 있습니다. - 일관성: 모든 로그가 동일한 키-값 구조를 가져 기계적인 파싱이 쉬워집니다. - 확장성: 새로운 메타데이터 필드가 필요할 때 로거 호출 시 해시에 키를 추가하기만 하면 즉시 반영됩니다. - 분석 효율: Splunk나 ELK Stack과 같은 도구에서 특정 사용자나 클래스별로 로그를 즉각적으로 필터링할 수 있습니다.