로거 설계: 프로덕션 환경에서 진단과 제어 우선
프로덕션 시스템에서 로거는 API 속도가 아니라 문제 발생 시 추가 부하 없이 빠르게 진단할 수 있는지로 평가받는다. 로깅은 오버헤드를 발생시키므로 정상 운영 중에는 최소화한다. 하지만 문제가 생기면 세부 정보 부족이나 뒤죽박죽 로그로 인해 문제 해결이 불가능해진다. 좋은 로거는 성능을 희생하지 않으면서도 진단 가능성을 보장한다.
문제는 로그 구조가 비즈니스 프로세스가 아닌 코드 구조를 따를 때 시작된다. 멀티스레드 시스템에서 서로 다른 태스크의 메시지가 섞여 특정 요청 추적이 어렵다.
태스크와 컨텍스트 연결
해결책은 호출 지점이 아닌 태스크에 로그를 연결하는 것이다. 매개변수로 컨텍스트를 전달하면 함수 시그니처가 지저분해진다. 실행 스레드에 바인딩하는 게 더 낫다.
logme에서는 스레드 채널로 구현한다:
auto requestCh = Logme::Instance->CreateChannel(Logme::ID("req-42"));
LogmeThreadChannel(requestCh);
HandleRequest();
내부 코드는 자동으로 채널에 로그:
void ParseHeaders() {
LogmeW("invalid header");
}
이제 로그가 작업 단위로 그룹화된다. spdlog와 Quill은 로거 중심 컨텍스트로 더 간단하지만 태스크에는 덜 적합하다.
또 다른 문제는 반복 메시지의 노이즈다. 재시도 오류로 로그가 중복으로 넘친다. 이벤트 수준 제한으로 해결:
LogmeW(LOGME_ONCE4THIS, "disk is full");
클래스 인스턴스당 한 번만 출력되어 로그가 깔끔하다.
유연한 메시지 라우팅
전역 설정만으로는 부족하다. HTTP/2에서 연결 로그와 요청 로그를 분리해야 한다. 요청 채널이 프로토콜 채널에 연결되지만 요청 세부 사항은 메인 로그를 어지럽히지 않아야 한다.
Logme::Override ovr;
ovr.Add.DisableLink = true;
LogmeI(ovr, "request-specific detail");
이렇게 하면 메시지가 로컬 채널에만 출력된다. 일반 이벤트는 어디서나 보이고, 사적 이벤트는 로컬에 머문다.
세밀한 상세를 위한 서브시스템
하나의 차원(채널)으로는 부족하다. 태스크에는 캐시, 네트워크, 파싱 같은 서브시스템이 있다. 선택적 로깅:
LOGME_SUBSYSTEM(cacheSid, "cache");
LOGME_SUBSYSTEM(parserSid, "parser");
LogmeD(cacheSid, "cache miss");
LogmeD(parserSid, "header parsed");
런타임에 캐시 서브시스템만 활성화. 나머지는 침묵하며 로그를 간결하게 유지한다.
라이브러리 통합
서드파티 라이브러리는 자체 로깅을 가진다. 이를 가로채 통합:
void FfmpegLogCallback(void* ptr, int level, const char* fmt, va_list args) {
char buffer[2048];
vsnprintf(buffer, sizeof(buffer), fmt, args);
Logme::Override ovr;
ovr.Add.DisableLink = true;
LogmeW(ovr, "[ffmpeg] %s", buffer);
}
메시지는 동일 규칙 적용: 채널, 서브시스템, 중복 제거.
포맷 지원으로 마이그레이션 용이:
LogmeI("value=%i", value);
fLogmeI("value={}", value);
LogmeI() << "value=" << value;
logme는 fmt와 스트림 스타일을 모두 지원. spdlog(fmt만), Quill(비동기 fmt)과 다르다.
재시작 없는 프로덕션 제어
항상 켜진 시스템에서 로그는 평소 최소. 사고 시:
- 캐시 의심? 캐시 서브시스템 활성화.
- 채널로 태스크 격리.
- 반복 제한.
logmectl로 동적 제어: 레벨, 서브시스템, 재시작 불필요.
수리 후 세부 사항 줄인다. 전역 로그를 과도하게 올리면 오버로드.
spdlog는 속도, Quill은 비동기, logme는 메시지 흐름 제어로 성능 유지.
핵심 요약:
- 채널로 태스크 단위 그룹화, 로거 아님.
- 이벤트 수준 중복 제거로 노이즈 감소.
- 서브시스템으로 런타임 세밀 제어.
- 라이브러리 통합으로 출력 표준화.
- 재시작 없는 동적 제어 필수.
— Editorial Team
아직 댓글이 없습니다.