안녕하세요! 최신 분산 시스템 환경에서 서비스의 동작을 추적하고 문제를 해결하는 것은 정말 중요한 과제죠. 이때 우리를 도와주는 강력한 도구가 바로 OpenTelemetry(OTel)입니다.
OpenTelemetry는 수많은 마이크로서비스에서 발생하는 요청의 흐름(Trace)을 시각화하고 분석할 수 있게 해주는데요, 이 흐름을 구성하는 가장 작은 작업 단위를 스팬(Span)이라고 부릅니다. 🗺️
오늘은 이 스팬의 "상태"를 나타내는 SpanStatus에 대해 깊이 있게 알아보려고 합니다. 단순히 "성공"과 "실패"로만 나누기에는 조금 더 디테일한 이야기가 숨어있답니다. 이 세 가지 상태 코드의 차이점을 명확히 이해하면, 시스템을 훨씬 더 정교하게 모니터링할 수 있게 될 거예요

🧐 전체적인 그림: 스팬과 그 상태는 왜 중요할까?
애플리케이션에서 특정 기능(예: '상품 주문 처리')이 실행되면, 이 과정은 하나의 거대한 트레이스(Trace)로 기록됩니다. 그리고 이 트레이스는 여러 개의 작은 작업 단위인 스팬(Span)으로 구성되죠.
- 스팬 1: API 요청 수신
- 스팬 2: 사용자 인증 확인
- 스팬 3: 데이터베이스에서 상품 정보 조회
- 스팬 4: 결제 서비스 호출 💳
- 스팬 5: 주문 완료 응답 전송
여기서 각 스팬이 성공적으로 끝났는지, 아니면 중간에 문제가 생겼는지 알아야 전체 '상품 주문 처리' 흐름의 병목이나 오류 지점을 정확히 찾아낼 수 있습니다. 바로 이때 SpanStatus가 핵심적인 역할을 합니다.
SpanStatus는 각 작업 단위의 결과를 알려주는 신호등과 같습니다. 이제 신호등의 세 가지 색깔을 하나씩 살펴볼까요?
1️⃣ Unset: 기본 상태 (🤔 회색 신호등)
Unset은 스팬이 생성될 때의 기본값입니다. 어떤 상태도 명시적으로 설정되지 않았다는 의미죠.
- 의미: "일단 별다른 오류는 없었어. 하지만 내가 성공했다고 보장하는 건 아니야."
- 특징: 대부분의 OpenTelemetry 백엔드(Jaeger, Zipkin 등)에서는 Unset 상태를 성공으로 간주하고 처리합니다. 오류가 아니면 성공으로 보는 것이죠.
- 언제 사용되나?: 스팬이 시작되고, 아무런 오류 없이 정상적으로 종료되었지만 개발자가 굳이 Ok 상태를 설정하지 않은 경우 이 상태로 남게 됩니다.
간단한 정보 조회처럼 실패해도 치명적이지 않거나, 성공 여부를 명시적으로 기록할 필요가 없는 작업에 적합합니다.
2️⃣ Ok: 명시적인 성공 (✅ 녹색 신호등)
Ok는 작업이 명시적으로 성공했음을 나타냅니다. 이것이 바로 Unset과의 가장 큰 차이점입니다.
- 의미: "이 작업은 내가 의도한 대로 완벽하게 성공했어!"
- 특징: 개발자가 코드 내에서 span.set_status(StatusCode.OK)와 같이 의도를 가지고 설정해야 합니다.
- 왜 중요할까?: 결제 처리, 회원 가입, 중요 데이터 저장 등 반드시 성공 여부를 확인해야 하는 비즈니스 로직에서 매우 유용합니다. 나중에 트레이스를 분석할 때, '오류가 없었던' 스팬과 '명확히 성공한' 스팬을 구분하여 볼 수 있기 때문에 데이터의 신뢰성이 올라갑니다.
예를 들어, "지난 한 시간 동안 결제 성공률은?"과 같은 질문에 정확히 답하려면 Ok 상태로 기록된 스팬을 집계해야 합니다.
3️⃣ Error: 명시적인 오류 (❌ 빨간색 신호등)
Error는 이름에서 알 수 있듯이, 작업 처리 중 오류가 발생했음을 나타냅니다.
- 의미: "문제가 발생했어! 확인해줘!"
- 특징: 보통 try-except 구문에서 예외(exception)가 발생했을 때 설정합니다. 단순히 상태만 Error로 바꾸는 것뿐만 아니라, 오류 메시지나 스택 트레이스를 스팬에 함께 기록(span.record_exception(e))하여 디버깅을 돕는 것이 일반적입니다.
- 언제 사용되나?: 네트워크 타임아웃, 데이터베이스 연결 실패, 잘못된 파라미터로 인한 예외 등 모든 예상치 못한 문제 상황에서 사용됩니다.
💻 코드로 이해하는 SpanStatus의 흐름
말로만 설명하면 감이 잘 안 올 수 있죠. Python OpenTelemetry SDK를 사용한 간단한 예시 코드를 통해 전체 프로세스를 이해해 보겠습니다.
from opentelemetry import trace
from opentelemetry.trace import StatusCode
# OTel Tracer 설정 (이 부분은 미리 설정되어 있다고 가정합니다)
tracer = trace.get_tracer(__name__)
def process_payment(amount: int, card_number: str):
# 'process_payment' 라는 이름으로 새로운 스팬을 시작합니다.
# 스팬이 시작되는 이 시점의 기본 상태는 'Unset' 입니다.
with tracer.start_as_current_span("process_payment") as span:
try:
span.set_attribute("payment.amount", amount)
span.add_event("Payment processing started")
if not card_number.startswith("4"):
# 유효하지 않은 카드 번호인 경우 예외 발생
raise ValueError("Invalid card number")
# --- 여기에 실제 결제 로직이 들어갑니다 ---
print(f"Processing payment of {amount}...")
# --- 결제 성공 ---
# ⭐️ 작업이 성공적으로 완료되었음을 명시적으로 기록합니다.
# 이 코드가 실행되면 스팬 상태는 'Unset' -> 'Ok'로 변경됩니다.
span.set_status(StatusCode.OK)
span.add_event("Payment successful!")
return {"status": "success"}
except Exception as e:
# 🚨 예외가 발생하면 오류 상태로 설정합니다.
# 스팬 상태는 'Unset' -> 'Error'로 변경됩니다.
error_message = f"Failed to process payment: {e}"
span.set_status(StatusCode.ERROR, error_message)
# 어떤 예외가 발생했는지 스팬에 기록하면 나중에 분석하기 좋습니다.
span.record_exception(e)
return {"status": "failed", "error": error_message}
# 함수 호출 예시
process_payment(100, "4123-...") # 성공 케이스
process_payment(50, "5432-...") # 실패 케이스
📊 결과 스팬 Raw Data 비교
위 코드가 실행된 후 수집된 스팬 데이터는 다음과 같은 형태를 띨 것입니다. (JSON 형식으로 단순화)
1. 성공 케이스 (Ok)
{
"name": "process_payment",
"context": { ... },
"attributes": {
"payment.amount": 100
},
"events": [
{"name": "Payment processing started", ...},
{"name": "Payment successful!", ...}
],
"status": {
"status_code": "OK"
},
...
}
2. 실패 케이스 (Error)
{
"name": "process_payment",
"context": { ... },
"attributes": {
"payment.amount": 50
},
"events": [
{"name": "Payment processing started", ...},
{
"name": "exception",
"attributes": {
"exception.type": "ValueError",
"exception.message": "Invalid card number",
...
}
}
],
"status": {
"status_code": "ERROR",
"description": "Failed to process payment: Invalid card number"
},
...
}
데이터를 보면 status_code가 어떻게 명확하게 OK와 ERROR로 나뉘는지, 그리고 오류 발생 시 상세 설명(description)과 예외 정보(exception 이벤트)가 어떻게 추가되는지 확인할 수 있습니다.
✨ 결론: 신호등을 올바르게 사용하자!
SpanStatus의 세 가지 상태를 정리해 보겠습니다.
- Unset 🤔: 기본값. 오류가 없으면 성공으로 간주되지만, 보장된 성공은 아님.
- Ok ✅: 명시적인 성공. "이 작업은 확실히 성공했다"는 강력한 신호.
- Error ❌: 명시적인 실패. 문제 발생을 알리고 디버깅에 필요한 정보를 담는 상태.
단순히 오류만 잡는 것을 넘어, 중요한 작업의 성공을 Ok로 명시적으로 기록하는 습관을 들이는 것이 좋습니다. 이렇게 하면 시스템의 동작을 훨씬 더 명확하고 신뢰성 있게 파악할 수 있으며, 장애가 발생했을 때 문제의 원인을 빠르고 정확하게 찾아내는 강력한 무기가 될 것입니다.
'클라우드 > opentelemetry' 카테고리의 다른 글
| OpenTelemetry의 심장: 이벤트 모델 파헤치기 Instrumented Raw Data의 여정 🚀 (0) | 2025.11.03 |
|---|---|
| 🚀 스마트한 원격 분석(Telemetry) 데이터 관리: 코드 수정 없이 백엔드 다루기 (0) | 2025.11.03 |
| 🚀 OpenTelemetry OTLP JSON: 트레이스, 메트릭, 로그 데이터 구조 완전 분석! (0) | 2025.10.18 |
| 📡 데이터 손실 없는 안전한 종료의 비결: OpenTelemetry Collector의 graceful_shutdown_timeout (0) | 2025.10.15 |
| 🧐 OpenTelemetry Collector: Core vs. Contrib, 완벽 비교 분석! 나에게 맞는 버전은? (0) | 2025.10.15 |