1. OpenTelemetry 직접 구현의 배경과 이점
공식 OpenTelemetry SDK는 매우 강력하지만, 수많은 추상화 계층과 패키지 의존성으로 인해 코드 베이스가 무거워질 수 있습니다. 특히 Edge Computing 환경이나 경량 마이크로서비스에서는 이러한 오버헤드가 부담이 됩니다. 핵심 로직을 약 200줄의 코드로 직접 구현하면 다음과 같은 이점을 얻을 수 있습니다.
- 의존성 최소화: 외부 라이브러리 없이 표준 프로토콜만으로 트레이싱을 수행하여 보안 및 유지보수 효율 증대
- 성능 최적화: 불필요한 기능을 제거하고 핵심적인 데이터 수집 및 전송 로직만 남겨 런타임 성능 향상
- 학습 및 제어: 트레이싱 데이터가 생성되고 전파되는 전 과정을 코드로 직접 제어함으로써 시스템에 대한 이해도 심화
2. 트레이싱 시스템을 구성하는 4가지 핵심 모듈
최소한의 트레이서를 구축하기 위해 반드시 구현해야 할 요소는 다음과 같습니다.
- 스팬(Span): 작업의 최소 단위입니다. 시작/종료 시간, 이름, 속성(Attributes), 그리고 고유한 Trace ID와 Span ID를 관리합니다. ID는 보통 16바이트(Trace)와 8바이트(Span)의 무작위 16진수 문자열로 생성합니다.
- 컨텍스트 관리(Context Management): 현재 실행 중인 스팬의 정보를 유지하고, 중첩된 하위 작업이 시작될 때 부모 스팬의 ID를 참조할 수 있도록 관리합니다. JavaScript 환경에서는
AsyncLocalStorage등을 활용하여 비동기 호출 간에도 컨텍스트를 유지합니다. - 전파(Propagation): 서로 다른 서비스 간에 트레이스 정보를 전달하는 과정입니다. W3C Trace Context 표준인
traceparent헤더(예:00-traceid-spanid-01)를 생성하고 해석하는 로직이 필요합니다. - 내보내기(Exporting): 수집된 데이터를 수집기(Collector)로 전송합니다. 복잡한 gRPC 대신 HTTP/JSON을 사용하는 OTLP(OpenTelemetry Line Protocol)를 구현하여 간단한
fetch요청으로 데이터를 전송할 수 있습니다.
3. OTLP/HTTP 프로토콜과 데이터 구조
데이터 전송의 핵심은 OTLP 사양에 맞는 JSON 구조를 만드는 것입니다. 전체 구조는 resourceSpans, scopeSpans, spans의 3단계 계층으로 구성됩니다.
- Resource: 서비스 이름(service.name)과 같은 공통 메타데이터를 포함합니다.
- Scope: 트레이서의 이름과 버전 정보를 나타냅니다.
- Span Details: 실제 작업의 타임스탬프(나노초 단위 정밀도), 상태 코드, 속성 값들이 포함됩니다. 이때 타임스탬프는 정밀한 측정을 위해
performance.now()와Date.now()를 조합하여 계산하는 것이 권장됩니다.
4. 실전 구현 전략 및 고려사항
200줄 이내로 코드를 유지하기 위해서는 다음과 같은 간소화 전략이 필요합니다. 첫째, 복잡한 샘플링 알고리즘 대신 100% 수집 또는 단순 확률 기반 샘플링을 적용합니다. 둘째, 데이터 전송 시 재시도 로직이나 배치(Batch) 처리를 최소화하고 즉시 전송하거나 간단한 큐를 사용합니다. 셋째, 오류 처리는 최소한의 로깅으로 대체하여 코드의 가독성을 유지합니다. 이러한 접근법은 실제 프로덕션에서 발생할 수 있는 복잡한 시나리오를 모두 커버하지는 못하더라도, 시스템의 흐름을 파악하는 데에는 충분한 성능을 발휘합니다.