OpenTelemetry(OTel)는 분산 시스템의 관측 가능성(Observability)을 확보하기 위한 표준화된 도구, API 및 SDK 세트입니다. 여기서 생성된 모든 귀중한 데이터(트레이스, 메트릭, 로그)는 OTLP(OpenTelemetry Protocol)라는 표준 프로토콜을 통해 수집 서버로 전송됩니다. 이 OTLP의 전송 포맷 중 하나가 바로 JSON 형식인데요! 📜
오늘은 OTel에서 수집하는 세 가지 핵심 데이터가 OTLP JSON 형태로 어떻게 구조화되어 있는지, 그 Raw 데이터의 심층적인 구조를 예시와 함께 파헤쳐 보겠습니다. 이 구조를 이해하면 데이터 분석 및 처리 능력이 한층 강화될 거예요! 💪

🔍 OTLP 데이터의 공통 계층 구조 이해하기
OTLP 데이터는 트레이스, 메트릭, 로그 종류에 상관없이 공통적인 계층 구조를 가집니다. 이는 데이터를 생성한 환경에 대한 정보를 명확하게 분리하고 전달하기 위함입니다.
| 데이터 종류 | 루트 객체 | 주요 하위 필드 | 데이터 내용 |
| Trace (트레이스) | "resourceSpans" | "scopeSpans", "spans" | 단일 요청의 실행 흐름 |
| Metric (메트릭) | "resourceMetrics" | "scopeMetrics", "metrics" | 시스템 성능 및 수치 데이터 |
| Log (로그) | "resourceLogs" | "scopeLogs", "logRecords" | 애플리케이션 로그 메시지 |
🌳 1. Resource 블록 (최상위 식별자)
모든 OTLP 데이터는 resource 블록을 포함합니다. 이는 데이터를 생성한 엔티티(서비스, 호스트 등)를 식별하는 데 사용됩니다. 즉, 이 데이터가 어디에서 왔는지를 알려주는 중요한 정보입니다.
- "attributes": 서비스 이름(service.name), 인스턴스 ID(service.instance.id), 클라우드 정보(cloud.provider) 등 환경 정보를 키-값 쌍으로 담고 있습니다.
📦 2. Scope/Instrumentation Library 블록
resource 바로 아래에는 scope 또는 instrumentation library 블록이 위치합니다.
- "scope": 어떤 라이브러리나 버전을 사용하여 이 데이터를 수집했는지 명시합니다. 예: io.opentelemetry.instrumentation.http, 버전 1.27.0.
📊 1. 전체 트레이스 데이터 구조 분석 (OTLP Trace JSON)
트레이스 데이터는 단일 요청이 시스템을 통과하며 거치는 모든 단계를 추적합니다. 핵심 요소는 스팬(Span)입니다.
{
"resourceSpans": [
{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "checkout-service" } },
{ "key": "service.instance.id", "value": { "stringValue": "627cc493-f310-47de-96bd-71410b7dec09" } },
{ "key": "cloud.provider", "value": { "stringValue": "aws" } },
{ "key": "host.id", "value": { "stringValue": "ip-172-31-12-55" } }
]
},
"scopeSpans": [
{
"scope": {
"name": "io.opentelemetry.instrumentation.http",
"version": "1.27.0"
},
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP POST /checkout",
"kind": "SPAN_KIND_SERVER",
"startTimeUnixNano": "1729123548123456789",
"endTimeUnixNano": "1729123549223456789",
"attributes": [
{ "key": "http.method", "value": { "stringValue": "POST" } },
{ "key": "http.status_code", "value": { "intValue": 200 } },
{ "key": "net.peer.ip", "value": { "stringValue": "10.10.1.12" } },
{ "key": "user.id", "value": { "stringValue": "abcd-123" } }
],
"events": [
{
"timeUnixNano": "1729123548156789000",
"name": "db.query.start",
"attributes": [
{ "key": "db.statement", "value": { "stringValue": "SELECT * FROM orders WHERE id=42" } }
]
}
],
"links": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "2233445566778899",
"attributes": [
{ "key": "linked.operation", "value": { "stringValue": "payment-processing" } }
]
}
],
"status": { "code": "STATUS_CODE_OK", "message": "completed successfully" }
}
]
}
]
}
]
}
✨ 주요 필드 상세 해설
| 필드 | 설명 | 예시 값 |
| "traceId" | 전체 트랜잭션을 식별하는 고유 ID. | 4bf92f3577b34da6a3ce929d0e0e4736 |
| "spanId" | 현재 작업 단계를 식별하는 고유 ID. | 00f067aa0ba902b7 |
| "name" | 스팬이 나타내는 작업의 이름. | HTTP POST /checkout |
| "kind" | 스팬의 유형 (서버, 클라이언트, 내부 등). | SPAN_KIND_SERVER |
| "startTimeUnixNano", "endTimeUnixNano" | 작업의 시작 및 종료 시간 (나노초). ⏱️ | 1729123548... |
| "attributes" | 스팬과 관련된 추가 상세 정보 (HTTP 메서드, 상태 코드, 사용자 ID 등). | http.method: POST |
| "events" | 스팬 실행 중 특정 시점에 발생한 이벤트 기록 (로그 메시지, DB 쿼리 시작 등). | name: db.query.start |
| "links" | 이 스팬과 관련된 다른 트레이스/스팬을 연결하는 정보 (분산 트레이싱 시 유용). 🔗 | linked.operation: payment-processing |
| "status" | 스팬의 실행 결과 (성공/에러 코드 및 메시지). | code: STATUS_CODE_OK |
예시 하이라이트: checkout-service의 HTTP POST 요청 처리(traceId: 4bf92f...) 중 DB 쿼리 이벤트(db.query.start)가 발생했고, 최종적으로 성공(STATUS_CODE_OK)했음을 보여줍니다.
📈 2. 전체 메트릭 데이터 구조 분석 (OTLP Metrics JSON)
메트릭 데이터는 시간의 경과에 따른 시스템 성능이나 상태를 수치로 나타냅니다. 게이지(Gauge), 합계(Sum), 히스토그램(Histogram) 등의 다양한 형태로 존재합니다.
{
"resourceMetrics": [
{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "checkout-service" } },
{ "key": "service.version", "value": { "stringValue": "1.5.2" } },
{ "key": "host.name", "value": { "stringValue": "ip-172-31-12-55" } }
]
},
"scopeMetrics": [
{
"scope": {
"name": "io.opentelemetry.runtime",
"version": "1.27.0"
},
"metrics": [
{
"name": "request_duration_seconds",
"description": "HTTP request duration",
"unit": "s",
"sum": {
"dataPoints": [
{
"attributes": [
{ "key": "http.method", "value": { "stringValue": "POST" } },
{ "key": "http.status_code", "value": { "intValue": 200 } }
],
"startTimeUnixNano": "1729123548123456000",
"timeUnixNano": "1729123549223466000",
"value": 0.154,
"flags": 1
}
],
"aggregationTemporality": "AGGREGATION_TEMPORALITY_DELTA",
"isMonotonic": false
}
},
{
"name": "system.cpu.utilization",
"description": "CPU usage percentage",
"unit": "%",
"gauge": {
"dataPoints": [
{
"attributes": [
{ "key": "host.id", "value": { "stringValue": "ip-172-31-12-55" } }
],
"timeUnixNano": "1729123549123459999",
"value": 73.4
}
]
}
}
]
}
]
}
]
}
✨ 주요 필드 상세 해설
| 필드 | 설명 | 예시 값 |
| "name", "description", "unit" | 메트릭의 이름, 설명, 측정 단위. | request_duration_seconds, s |
| "sum", "gauge" | 메트릭의 유형과 그 값이 포함된 블록. | sum (누적 합계), gauge (순간 값) |
| "dataPoints" | 실제 측정된 데이터 값들의 목록. | 📝 |
| dataPoints."attributes" | 해당 측정값에 특화된 태그/필터링 정보 (예: HTTP 메서드, 상태 코드). | http.status_code: 200 |
| dataPoints."value" | 측정된 실제 수치. | 0.154 (초), 73.4 (%) |
| dataPoints."timeUnixNano" | 측정값이 수집된 시간. ⏰ | 1729123549... |
| sum."aggregationTemporality" | 합계 측정값의 집계 방식 (델타: 이전 간격과의 차이, 누적: 시작부터의 합). | AGGREGATION_TEMPORALITY_DELTA |
예시 하이라이트: 요청 처리 시간(request_duration_seconds)은 합계(sum)로 0.154초가 측정되었고, 시스템 CPU 사용률(system.cpu.utilization)은 게이지(gauge)로 73.4%가 측정되었음을 보여줍니다.
📝 3. 전체 로그 데이터 구조 분석 (OTLP Logs JSON)
로그 데이터는 애플리케이션에서 발생하는 이벤트를 기록하는 텍스트 기반의 메시지입니다. OTel에서는 로그에도 트레이싱 정보를 연결하여 활용도를 높입니다.
{
"resourceLogs": [
{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "checkout-service" } },
{ "key": "service.namespace", "value": { "stringValue": "web-shop" } },
{ "key": "host.id", "value": { "stringValue": "ip-172-31-12-55" } }
]
},
"scopeLogs": [
{
"scope": {
"name": "io.opentelemetry.logger",
"version": "1.27.0"
},
"logRecords": [
{
"timeUnixNano": "1729123548123456789",
"observedTimeUnixNano": "1729123548125456789",
"severityText": "INFO",
"severityNumber": 9,
"body": { "stringValue": "Order ID 42 successfully processed" },
"attributes": [
{ "key": "order.id", "value": { "intValue": 42 } },
{ "key": "user.id", "value": { "stringValue": "abcd-123" } },
{ "key": "event.name", "value": { "stringValue": "order_completed" } }
],
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"flags": 1
},
{
"timeUnixNano": "1729123549223456000",
"severityText": "ERROR",
"severityNumber": 17,
"body": { "stringValue": "Payment gateway timeout error" },
"attributes": [
{ "key": "payment.processor", "value": { "stringValue": "stripe" } },
{ "key": "error.code", "value": { "intValue": 504 } }
],
"traceId": "7aa111223344556677889900aabbccdd",
"spanId": "11aabbccddeeff00",
"flags": 1
}
]
}
]
}
]
}
✨ 주요 필드 상세 해설
| 필드 | 설명 | 예시 값 |
| "timeUnixNano", "observedTimeUnixNano" | 로그가 생성된 시간 및 수집기가 관찰한 시간. | 1729123548... |
| "severityText", "severityNumber" | 로그의 심각도 레벨 (텍스트 및 수치). | INFO (9), ERROR (17) |
| "body" | 실제 로그 메시지 내용. | Order ID 42 successfully processed |
| "attributes" | 로그 메시지와 관련된 추가 정보. | order.id: 42, user.id: abcd-123 |
| "traceId", "spanId" | 이 로그를 발생시킨 트레이스와 스팬의 ID. (Observability 핵심!) 🧩 | 4bf92f35..., 00f067aa... |
예시 하이라이트: 주문 성공 로그(severityText: INFO)는 특정 트레이스/스팬 ID에 연결되어 있습니다. 또한, 결제 게이트웨이 오류(severityText: ERROR) 로그도 심각도 레벨 17과 함께 기록되어 있습니다.
💡 요약 및 결론
OpenTelemetry의 OTLP JSON 포맷은 모든 관측 데이터를 표준화된 계층 구조로 묶어 효율적인 전송과 처리를 가능하게 합니다.
- 리소스(Resource): 데이터의 출처 (서비스, 호스트 정보)를 명확히 합니다.
- 스코프(Scope): 데이터 수집에 사용된 라이브러리 정보를 명시합니다.
- 데이터: 트레이스(스팬), 메트릭(데이터 포인트), 로그(로그 레코드) 등 실제 내용이 담겨 있습니다.
이러한 명확하고 상세한 구조 덕분에, OTel을 사용하는 시스템은 분산된 환경에서도 데이터를 쉽게 수집, 분석, 시각화하여 운영 상황을 완벽하게 파악할 수 있게 됩니다! 👍
'클라우드 > opentelemetry' 카테고리의 다른 글
| 🚀 스마트한 원격 분석(Telemetry) 데이터 관리: 코드 수정 없이 백엔드 다루기 (0) | 2025.11.03 |
|---|---|
| 🚦 OpenTelemetry Span 상태 완벽 정복: Unset, Ok, Error 파헤치기 (0) | 2025.10.18 |
| 📡 데이터 손실 없는 안전한 종료의 비결: OpenTelemetry Collector의 graceful_shutdown_timeout (0) | 2025.10.15 |
| 🧐 OpenTelemetry Collector: Core vs. Contrib, 완벽 비교 분석! 나에게 맞는 버전은? (0) | 2025.10.15 |
| OpenTelemetry 샘플링 완벽 정복: Trace ID 기반 10% 샘플링 설정하기 🚀 (0) | 2025.10.14 |