개발자라면 누구나 디버깅을 위해 print나 console.log를 사용해 본 경험이 있을 겁니다. 하지만 서비스가 복잡해지고 여러 개의 마이크로서비스로 구성되기 시작하면, 이 로그들은 골칫거리가 되기 시작합니다. 🤯
- "이 로그는 대체 어느 서버에서 온 거지?"
- "에러 로그는 있는데, 이 요청이 어떤 과정을 거쳤는지 알 수가 없네..."
- "서비스마다 로그 형식이 제각각이라 분석하기 너무 힘들어!"
이런 고민을 해결하기 위해 등장한 것이 바로 관측 가능성(Observability)의 표준, OpenTelemetry(OTEL)입니다. 오늘은 OTEL이 어떻게 로그를 체계적으로 수집하고 관리하는지, 그 비밀스러운 내부 동작 방식을 알기 쉽게 파헤쳐 보겠습니다.

🗺️ 전체 그림 보기: 로그 데이터는 어떻게 여행할까?
개별 구성 요소를 살펴보기 전에, 먼저 전체적인 데이터 흐름, 즉 파이프라인을 이해하는 것이 중요합니다. OTEL의 로깅 시스템은 마치 잘 짜인 공장의 생산 라인과 같습니다.
공장 설립 (Provider) → 제품 생산 (Generator) → 품질 검수 및 포장 (Processor) → 배송 (Exporter)
이 네 가지 단계를 거쳐 애플리케이션에서 발생한 로그가 최종 목적지까지 안전하게 전달됩니다. 이 구조는 로그뿐만 아니라 트레이스(Trace), 메트릭(Metrics)에서도 동일하게 적용되어 일관성을 유지하는 것이 OTEL의 핵심 철학입니다!
이제 각 단계가 어떤 역할을 하는지 자세히 알아볼까요?
🛠️ OpenTelemetry 로깅 시스템의 핵심 부품 4가지
1. Logger Provider (로거 제공자): 모든 것의 시작, 컨트롤 타워 🏭
LoggerProvider는 우리 로깅 시스템의 총괄 관리자이자 설계자입니다.
- 역할: Logger(실제 로그를 찍는 객체)를 생성하고 관리하는 팩토리(Factory) 역할을 합니다.
- 핵심 기능: 애플리케이션 전체에서 생성되는 모든 로그에 일관된 설정을 적용합니다. 예를 들어, "이 애플리케이션의 이름은 '주문 서비스'이고, 버전은 '1.2.0'이다"와 같은 리소스(Resource) 정보를 모든 로그에 자동으로 붙여줍니다.
🤔 이게 왜 중요할까요? LoggerProvider가 없다면, 개발자들은 로그를 남길 때마다 서비스 이름이나 버전 같은 정보를 수동으로 추가해야 할 겁니다. 이는 실수하기 쉽고 매우 비효율적이죠. LoggerProvider는 이러한 공통 정보를 중앙에서 관리하여 모든 로그가 일관된 정체성을 갖도록 보장합니다.
2. Logger (로거): 현장에서 뛰는 일꾼 👷
Logger는 개발자가 코드에서 실제로 로그를 기록하기 위해 사용하는 객체입니다.
- 역할: "사용자 로그인 성공", "데이터베이스 연결 실패"와 같은 실제 로그 메시지, 즉 LogRecord를 생성하는 주체입니다.
- 사용 예시:
# LoggerProvider로부터 Logger를 받아옵니다. logger = provider.get_logger("my-app-logger") # 이 Logger를 사용해 로그를 기록합니다. logger.info("새로운 주문이 접수되었습니다.")
개발자는 LoggerProvider의 복잡한 내부 구조를 알 필요 없이, 그저 이 Logger를 가져와 필요한 메시지만 남기면 됩니다.
3. Log Record (로그 레코드): 정보가 담긴 스마트 데이터 박스 📦
LogRecord는 단순한 텍스트 메시지가 아닙니다. 하나의 로그 이벤트에 대한 모든 정보를 담고 있는 구조화된 데이터입니다.
- 역할: 단일 로그 이벤트의 모든 세부 정보를 포함합니다.
- 포함 정보:
- Body: 실제 로그 메시지 (예: "결제 처리 완료")
- Timestamp: 로그가 발생한 시간
- Severity: 로그의 심각도 (INFO, WARN, ERROR 등)
- Attributes: 추가적인 정보 (예: user_id: 123, order_id: "A-5678")
- Context: 가장 중요한 부분! 🤩 trace_id, span_id 같은 컨텍스트 정보가 포함되어 있어, 이 로그가 어떤 요청 처리 과정(Trace) 중에 발생했는지 정확하게 연결해 줍니다.
Raw Data 예시
하나의 LogRecord가 실제로 어떻게 생겼는지 JSON 형태로 살펴볼까요?
{
"Timestamp": "2025-11-11T09:36:00.123456789Z",
"ObservedTimestamp": "2025-11-11T09:36:00.123456789Z",
"TraceId": "0x5b8aa5a2d2c872e8321cf37308d69df2",
"SpanId": "0x14214c1a4a2ac7c1",
"SeverityText": "Info",
"SeverityNumber": 9,
"Body": "사용자(id: 123)가 장바구니에 상품(id: 456)을 추가했습니다.",
"Attributes": {
"userId": 123,
"productId": 456,
"http.method": "POST",
"http.route": "/cart/items"
},
"Resource": {
"service.name": "cart-service",
"service.version": "1.1.0"
}
}
보시다시피, 단순한 텍스트가 아니라 어떤 서비스(cart-service)에서 발생했고, 어떤 요청(TraceId)과 관련 있는지 모든 정보가 풍부하게 담겨 있습니다. 이것이 바로 OTEL 로깅이 강력한 이유입니다.
4. Log Record Exporter (로그 레코드 내보내기): 최종 목적지로 배송 🚚
LogRecordExporter는 잘 만들어지고 처리된 로그 레코드들을 외부 시스템으로 전송하는 역할을 합니다.
- 역할: 처리된 로그 데이터를 최종 목적지로 내보냅니다.
- 주요 목적지:
- Loki, Elasticsearch 같은 로그 수집 및 분석 도구
- Datadog, New Relic 같은 상용 모니터링 솔루션
- 테스트나 디버깅을 위한 콘솔(Console) 출력
참고: 파이프라인 중간에는 LogRecordProcessor라는 단계가 있습니다. 이 프로세서는 로그를 외부로 내보내기 전에 배치(batch) 처리를 통해 네트워크 부하를 줄이거나, 특정 조건의 로그만 필터링하는 등 효율적인 중간 처리 작업을 수행합니다.
✨ 결론: 로그를 데이터처럼 다루기
OpenTelemetry 로깅 시스템은 단순한 텍스트 기록을 넘어, 로그를 풍부한 컨텍스트를 가진 구조화된 데이터로 취급합니다.
- Provider를 통해 모든 로그에 일관성을 부여하고,
- Logger로 손쉽게 로그를 생성하며,
- LogRecord에 트레이스 정보까지 담아 문제의 원인을 정확히 추적하고,
- Exporter를 통해 원하는 분석 시스템으로 손쉽게 전송합니다.
이제 console.log의 늪에서 벗어나, OpenTelemetry와 함께 시스템의 상태를 한눈에 파악하고 장애를 신속하게 해결하는 진정한 관측 가능성의 세계를 경험해 보세요!
'클라우드 > opentelemetry' 카테고리의 다른 글
| OpenTelemetry 메트릭, CUMULATIVE vs DELTA 헷갈리면 큰일나는 이유 🚨 (1) | 2025.11.11 |
|---|---|
| 당신의 OpenTelemetry Collector는 왜 느릴까? 😮 Headless Service로 완벽 해결! (0) | 2025.11.11 |
| 서버 터지기 전에 꼭 알아야 할 이것! 🤯 분산 시스템 문제, 대체 어디서부터 봐야 할까요? (1) | 2025.11.10 |
| 🤯 OTel Metrics, 아직도 헷갈리시나요? MeterProvider, Meter, Instrument 완벽 정리! (0) | 2025.11.10 |
| OpenTelemetry Collector, 아직도 run만 쓰세요? 고수들이 쓰는 5가지 필수 명령어 🚀 (1) | 2025.11.10 |