본문 바로가기
클라우드/opentelemetry

🚀 OpenTelemetry OTLP JSON: 트레이스, 메트릭, 로그 데이터 구조 완전 분석!

by gasbugs 2025. 10. 18.

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 포맷은 모든 관측 데이터를 표준화된 계층 구조로 묶어 효율적인 전송과 처리를 가능하게 합니다.

  1. 리소스(Resource): 데이터의 출처 (서비스, 호스트 정보)를 명확히 합니다.
  2. 스코프(Scope): 데이터 수집에 사용된 라이브러리 정보를 명시합니다.
  3. 데이터: 트레이스(스팬), 메트릭(데이터 포인트), 로그(로그 레코드) 등 실제 내용이 담겨 있습니다.

이러한 명확하고 상세한 구조 덕분에, OTel을 사용하는 시스템은 분산된 환경에서도 데이터를 쉽게 수집, 분석, 시각화하여 운영 상황을 완벽하게 파악할 수 있게 됩니다! 👍