Nova를 이용한 도메인 기반 C++ 로깅
Source: Dev.to
Nova 소개 – 도메인 기반 라우팅을 지원하는 결정론적 C++ 로깅 라이브러리
저장소: https://github.com/kmac-13/nova/
벤치마크 문서: https://github.com/kmac-13/nova/blob/main/docs/BENCHMARKS.md
Nova의 초기 릴리스를 발표하게 되어 기쁩니다. Nova는 결정론적 동작, 컴파일 타임 설정 가능성, 그리고 호스팅 플랫폼부터 베어 메탈, 안전 필수 환경까지 다양한 시스템을 위한 유연한 도메인 기반 라우팅을 중점으로 설계된 최신 C++ 로깅 라이브러리입니다.
이미 품질 좋은 C++ 로깅 라이브러리는 여러 개 존재합니다. 그러나 대부분의 로깅 라이브러리는 심각도 레벨을 중심으로 라우팅과 필터링을 구성하고, 전역 로거 설정이나 런타임 문자열 기반 카테고리에 의존합니다. 엔지니어는 종종 서브시스템의 동작을 제한된 심각도 레벨에 맞춰야 하며, 프로덕션에서 어떤 임계값을 활성화할지도 고민해야 합니다. 이 때문에 한 서브시스템에 대해 디버그 로깅을 켜면, 관련 없는 다른 영역까지 디버그 로깅이 켜지는 상황이 발생합니다.
Nova는 로깅 도메인을 컴파일 타임 타입으로 취급합니다. 이를 통해 로깅 설정과 라우팅이 애플리케이션 구조를 직접 반영하도록 하며, 서브시스템을 전역 심각도 카테고리에 억지로 맞출 필요가 없습니다.
- 도메인은 서브시스템, 모듈, 인터페이스, 클래스, 라이브러리 등 어떤 도메인‑특정 개념도 될 수 있으며, 각 도메인은 전역 설정에 의존하지 않고 독립적으로 활성화·비활성화·라우팅될 수 있습니다.
- 도메인이 문자열 식별자가 아니라 독립적인 타입이기 때문에, 라이브러리는 자체 로깅 도메인을 정의해도 애플리케이션이나 타사 로깅 설정을 방해하지 않습니다.
주요 특징
- 타입 기반 로깅 도메인 – 서브시스템, 모듈, 클래스, 라이브러리 등 어떤 개념에도 로그를 남길 수 있습니다.
- 컴파일 타임 비활성화 도메인 제거 – C++17에서는 언어 보장이, C++11/14에서는 최적화 의존성을 가집니다.
- 컴파일 타임 라우팅 – 전역 로거 레지스트리나 공유 런타임 설정에 의존하지 않습니다.
추가 목표
- 간단한 파이프라인 및 확장 가능한 API
- 결정론적 동작 – 힙 할당, 예외, RTTI 없음 (일부 편의 sink에서는 명시적으로 힙 사용을 문서화)
- 호스팅 시스템부터 베어 메탈까지 지원
- 실시간·멀티스레드 워크로드에서도 충분히 빠른 성능
Nova는 또한 Flare 라는 비동기‑시그널‑안전 크래시·포렌식 로깅 컴포넌트를 포함합니다. Flare는 시그널 핸들러에서 힙 할당, 락, 비시그널‑안전 C++ 런타임 기능 없이 구조화된 진단 레코드를 직접 디스크에 기록합니다.
#include <nova/logger.hpp>
// 도메인 정의 (어떤 타입이든 가능)
struct MotionPlanner {};
// 도메인에 이름(MOTION), 활성화 상태(true), 시계 타입(steadyNanosecs) 지정
NOVA_LOGGER_TRAITS( MotionPlanner, MOTION, true, kmac::nova::TimestampHelper::steadyNanosecs );
int main()
{
// motion planner sink 를 mpSink 로 설정
...
// mpSink 를 MotionPlanner 도메인에 바인드
kmac::nova::ScopedConfigurator config;
config.bind( &mpSink );
// 로그 출력
NOVA_LOG( MotionPlanner ) << "Planning trajectory...";
}
위 예시에서 MotionPlanner 도메인이 정의되고, 해당 도메인의 특성이 설정되며, 목표 sink가 도메인에 바인드된 뒤 로그가 기록됩니다. 여기서는 도메인이 빈 구조체이지만, 인터페이스, 추상 클래스, 구체 클래스 등 어떤 타입도 도메인으로 사용할 수 있습니다. 특정 클래스 자체를 도메인으로 지정하고, 그 클래스 범위 내에서만 로그를 제한할 수도 있습니다.
타입을 로깅 도메인으로 사용하면 컴파일 타임 라우팅, 강력한 서브시스템 분리, 도메인별 설정·활성화가 가능해집니다. 비활성화된 도메인은 컴파일러에 의해 완전히 제거될 수 있으며, 문자열 기반 라우팅에서 발생할 수 있는 무음 런타임 실패를 타입 이름이 방지합니다. 또한 도메인별 제어 덕분에 한 서브시스템에 대해 상세 로그를 켜도 다른 영역에는 전혀 영향을 주지 않으며, 전체 애플리케이션에 걸쳐 공유된 심각도 임계값을 올리거나 내릴 필요가 없습니다.
벤치마크
Nova는 Quill, spdlog 등 여러 인기 C++ 로깅 라이브러리와 비교 테스트를 진행했습니다. 테스트 항목은 다음과 같습니다.
- 다양한 스레드 수
- 고정 큐 크기
- 지속적인 처리량 시나리오
- 전달 보장 지연 테스트
- 실제 파일 sink와 카운팅 sink(입출력 오버헤드 분리) 모두 사용
벤치마크에서는 큐 크기와 백엔드 스레드 모델을 의도적으로 동일하게 맞춰 특정 라이브러리 구성이 구조적으로 유리해지는 것을 방지했습니다.
주요 결과(워크로드별 차이 존재)
- 보장된 전달 시나리오와 메모리 제한 구성에서 Nova가 특히 뛰어났습니다.
- Nova의 동기화된 sink는 다른 라이브러리의 동기식 구성보다 현저히 우수했습니다.
- Nova의 비동기 백엔드(I/O 없는 카운팅 sink 사용)는 스레드 수에 관계없이 경쟁력 있는 처리량을 보였으며, 레코드당 힙 할당이 전혀 없었습니다.
- Quill은 프론트엔드 enqueue 처리량이 매우 높았지만, 지속적인 과부하 상황에서는 상당한 드롭률을 보였습니다.
- spdlog의 async 모드는 무거운 멀티스레드 워크로드에서 성능 저하가 나타났습니다.
Nova는 이론상 가장 높은 프론트엔드 enqueue 속도를 항상 기록하지는 않지만, 현실적인 다양한 워크로드에서 특히 결정론적 동작과 메모리 사용 제한이 중요한 경우 매우 경쟁력 있게 동작합니다. 전체 벤치마크 방법론과 원시 데이터는 저장소에서 확인할 수 있습니다.
원칙
| 원칙 | 설명 |
|---|---|
| 도메인 기반 라우팅 | 로깅은 전역 심각도 카테고리에 서브시스템을 억지로 맞추는 것이 아니라, 애플리케이션 구조를 반영해야 합니다. |
| 결정론적 동작 | 힙 할당, 예외, RTTI를 설계 단계에서 배제합니다. 힙 사용이 필요한 경우(소수의 편의 sink 등) 명시적으로 문서화하고, Nova 핵심·Extras·Flare에서는 가능한 한 피합니다. |
| 명시적 트레이드오프 | 버퍼·큐 크기, 동기·비동기 전달, 블로킹 여부, 팬‑아웃 동작 등을 모두 가시화하고 설정 가능하게 합니다. |
| 컴파일 타임 설정 | 가능한 경우 로깅 설정을 컴파일 타임에 해결합니다. |
| 프로덕션 지향 설계 | 지속적이고 현실적인 워크로드와 운영 예측 가능성을 최우선으로 최적화합니다. |
| 현대 C++ | 불필요한 복잡성이나 무거운 의존성을 도입하지 않으면서 최신 언어 기능을 활용합니다. |
| 성능 의식 설계 | 특히 지연 민감·멀티스레드 시스템에서 로깅 오버헤드가 예측 가능하고 낮아야 합니다. |
초기 릴리스는 위에 링크된 저장소에서 확인할 수 있습니다. 현재 다음과 같은 추가 기능을 개발 중입니다.
- 다양한 sink/백엔드 추가
- 패키징 및 통합 마무리
- 바이너리 로깅
Nova에 대한 피드백(통합 경험, 크로스‑플랫폼·컴파일러 이슈, 기능 요청 등)을 언제든 환영합니다. 실제 프로젝트에 적용해 보시고 성능이나 개선점에 대해 알려주시면 감사하겠습니다.
읽어 주셔서 감사합니다.