dbt v2 텔레메트리와 관찰성
dbt v2 텔레메트리와 관찰성 (Telemetry and observability)
dbt v2는 dbt v1의 구조화 로깅을 대체하는 포괄적인 관찰성 시스템을 제공해요. OpenTelemetry 규칙 위에 구축되고 안정적인 protobuf 스키마로 뒷받침되어, 오케스트레이터·관찰성 플랫폼·커스텀 도구와 깊은 통합을 지원해요.
출처: 문서
본문
dbt v2는 dbt v1의 구조화 로깅을 대체하는 포괄적인 관찰성 시스템을 제공해요. OpenTelemetry 규칙 위에 구축되고 안정적인 protobuf 스키마로 뒷받침되어, 오케스트레이터·관찰성 플랫폼·커스텀 도구와 깊은 통합을 가능하게 해요.
--log-format, --log-level 같은 공유 CLI 로깅 구성은 Logs를 참고해 주세요.
이 시스템은 dbt가 dbt Labs로 보내는 익명 사용 통계와는 별개예요. 익명 사용 통계를 구성하려면 Anonymous usage stats를 참고해 주세요.
이 시스템은 dbt 플랫폼이 오케스트레이션·모니터링에 사용하는 것과 같은 통합을 사용해서, 대규모에서 검증되고 프로덕션 준비가 된 기능을 제공해요.
Available output formats
dbt v2 텔레메트리는 세 가지 출력 형식을 지원하며, 각각 독립적으로 활성화할 수 있어요:
| Format | Use case | Availability |
|---|---|---|
| JSONL | 실시간 모니터링, 다운스트림 시스템으로의 스트리밍 | 이벤트가 발생할 때 작성 |
| Parquet | 실행 후 분석, 쿼리, 장기 저장 | 실행이 완료될 때 작성 |
| OTLP | 관찰성 플랫폼(Datadog, Jaeger 등)과의 통합 | 실시간 스트리밍 |
텔레메트리 출력 활성화
텔레메트리 출력을 활성화하는 옵션 예시들(단일 실행에서 여러 출력을 결합할 수 있어요):
JSONL을 파일로 쓰기 (logs/ 디렉터리에 저장):
dbtf build --otel-file-name telemetry.jsonl
JSONL을 stdout으로 스트리밍:
dbtf build --log-format otel
Parquet 파일 쓰기 (target/metadata/ 디렉터리에 저장):
dbtf build --otel-parquet-file-name telemetry.parquet
전체 trace 레벨 로그로 Parquet 파일 쓰기(디버깅에 권장):
dbtf build --log-level-file trace --otel-parquet-file-name telemetry.parquet
OpenTelemetry collector로 내보내기:
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" dbtf build --export-to-otlp
플랫폼 작업 실행에서 텔레메트리 다운로드
dbt 플랫폼에서 dbt v2 작업 실행은 dbt 명령어 단계의 OTel 텔레메트리를 Parquet 아티팩트로 저장해요. 완료된 실행에서 Run summary 탭을 열고 단계를 선택한 뒤 Download > Download OTel log를 클릭하세요. 이 옵션은 OTel 파일을 생성한 dbt v2 실행에서만 나타나요. 단계별 지침은 Downloading logs를 참고해 주세요.
API로 텔레메트리 검색
dbt Administrative API v2를 통해 실행 단계의 OTel Parquet 아티팩트를 검색할 수도 있어요. 작업 완료 후 아티팩트를 다운로드할 수 있게 해 주죠. 이를 사용해 노드 결과·테스트 결과를 웨어하우스의 데이터 품질 프레임워크 같은 다운스트림 시스템으로 자동 수집할 수 있어요.
dbt v2 명령어 단계 중 텔레메트리를 생성하는 각 단계는 telemetry-STEP_NUMBER-otel.parquet 아티팩트를 써요. dbt deps 같은 일부 단계는 Parquet 아티팩트를 생성하지 않아요.
Retrieve Run Artifact 엔드포인트로 이 아티팩트를 가져올 수 있어요:
GET https://YOUR_ACCESS_URL/api/v2/accounts/ACCOUNT_ID/runs/RUN_ID/artifacts/metadata/telemetry-STEP_NUMBER-otel.parquet?step=STEP_NUMBER
YOUR_ACCESS_URL은 지역·플랜에 맞는 Access URL로, ACCOUNT_ID, RUN_ID, STEP_NUMBER는 자신의 값으로 바꾸세요. 서비스 계정 토큰 또는 개인 액세스 토큰으로 인증하세요.
curl로 할 수도 있어요:
curl --request GET \
--url 'https://YOUR_ACCESS_URL/api/v2/accounts/12345/runs/67890/artifacts/metadata/telemetry-4-otel.parquet?step=4' \
--header 'Authorization: Token ***' \
--output telemetry-4-otel.parquet
원하는 텔레메트리 아티팩트를 생성한 단계를 찾으려면 실행 상세 요청에 run_steps를 포함시켜 실행의 단계들을 나열해요:
GET https://YOUR_ACCESS_URL/api/v2/accounts/ACCOUNT_ID/runs/RUN_ID/?include_related=["run_steps"]
이 아티팩트는 OTel 로그를 방출한 dbt v2 단계에서만 검색할 수 있어요.
Telemetry data
dbt v2 텔레메트리는 두 가지 유형의 레코드를 담아요:
- Spans — 시작과 끝 시간이 있는 작업(모델 컴파일, 테스트 실행 등).
- Log records — span 내의 특정 시점 이벤트.
텔레메트리 계층 구조
모든 dbt 명령어는 span의 계층 구조를 만들어요:
Invocation (dbtf build)
├── Phase (Parse)
├── Phase (Compile)
│ ├── Node (model.project.customers)
│ └── Node (model.project.orders)
└── Phase (Run)
├── Node (model.project.customers)
└── Node (model.project.orders)
trace_id(일명 invocation_id)는 단일 dbt 명령어의 모든 텔레메트리 레코드에서 동일하므로 이벤트를 쉽게 연관지을 수 있어요.
Node outcome
모든 노드는 참여하는 각 단계에 대해 결과를 생성해요. parse 같은 일부 단계는 노드 레벨 실행을 포함하지 않으므로 노드 span이나 노드 결과를 생성하지 않아요.
node_outcome 필드는 dbt v2가 노드 작업을 실행했는지 여부를 나타내요.
| Outcome | Description |
|---|---|
| success | 노드 작업이 에러 없이 완료됨 |
| error | 노드 작업 실행 실패(예: 구문 에러) |
| skipped | 노드가 평가되지 않음(skip 이유 참고) |
| canceled | 노드가 중단됨(예: 사용자가 Ctrl+C 누름) |
Skip reasons
dbt v2가 노드를 건너뛸 때 텔레메트리는 이유를 포함해요:
| Skip reason | Description |
|---|---|
| upstream | 종속성이 실패함 |
| cached | dbt v2가 캐시에서 결과를 재사용함(dbt State로 변경 감지 안 됨) |
| phase_disabled | 단계가 비활성화됨(예: --static-analysis off) |
| noop | 노드가 이 단계에서 작업을 수행하지 않음(예: ephemeral 모델) |
Test outcomes
테스트가 성공적으로 실행되면(node_outcome: success) 테스트 결과를 보고해요:
| Test outcome | Description |
|---|---|
| passed | 실패가 감지되지 않음 |
| warned | 실패가 감지되었지만 경고로 구성됨 |
| failed | 실패가 감지됨(데이터 품질 문제) |
node_outcome: success와 test_outcome: failed인 테스트는 dbt v2가 테스트를 성공적으로 실행했고 테스트가 데이터 품질 문제를 보고했다는 뜻이에요. 이는 테스트 자체가 실행되지 못한 것을 뜻하는 node_outcome: error(예: 잘못된 SQL)와 다르다는 점을 기억하세요.
Querying telemetry data
텔레메트리 데이터를 쿼리해 dbt 실행에 대한 더 깊은 통찰을 얻어요.
JSONL 예시
JSONL 텔레메트리 데이터를 쿼리하는 예시들이에요. 에러를 실시간으로 감시:
tail -f telemetry.jsonl | jq 'select(.severity_text == "ERROR")'
건너뛴 노드·이유·업스트림 세부정보 나열:
cat telemetry.jsonl | jq 'select(.attributes.node_outcome == "NODE_OUTCOME_SKIPPED") | {node: .attributes.unique_id, reason: .attributes.node_skip_reason, upstream: .attributes.node_skip_upstream_detail.upstream_unique_id }'
dbt 플랫폼에서 텔레메트리 다운로드
dbt 플랫폼에서 작업을 실행했다면 실행 페이지의 Download 드롭다운으로 OpenTelemetry(OTEL) Parquet 아티팩트를 다운로드할 수 있어요. 다운로드에는 실행의 각 단계에 대한 telemetry-<step>-otel.parquet 파일이 포함돼요.
DuckDB로 Parquet 분석
DuckDB를 활용해 Parquet 파일에 저장된 텔레메트리 데이터를 더 잘 이해할 수 있어요. 처리 비용(processing cost) 기준으로 가장 느린 노드 찾기(커넥션 게이트에서의 벽시계 수명이 아니라):
import duckdb
duckdb.sql("""
SELECT
attributes.unique_id,
attributes.duration_ms,
attributes.idle_time_ms
FROM 'telemetry.parquet'
WHERE event_type LIKE '%NodeProcessed%'
AND attributes.duration_ms IS NOT NULL
ORDER BY attributes.duration_ms DESC
LIMIT 10
""").show()
올바른 타이밍 메트릭 선택하기 — 텔레메트리는 노드 성능을 측정하는 여러 방법을 제공해요:
- 처리 시간 (
attributes.duration_ms) — v2가 노드를 적극적으로 처리하는 데 쓴 시간으로, 중첩된NodeEvaluated작업을 포함해요. 업스트림 노드나 내부 백프레셔를 기다리는 시간은 제외돼요. 이 메트릭을 사용해 처리에 가장 오래 걸리는 노드를 식별하세요. - 노드 수명 (
end_time_unix_nano - start_time_unix_nano) — span의 시작부터 끝까지의 전체 시간으로, 커넥션-제한 게이트에서 대기한 시간을 포함해요. 스레드가 포화된 빌드에서는 이 메트릭이 가장 많은 처리 작업보다 긴 큐 시간을 가진 노드를 드러낼 수 있어요. - 유휴 시간 (
attributes.idle_time_ms) — 노드가 적극적으로 처리되지 않고 대기한 시간(예: 업스트림 노드나 사용 가능한 처리 용량을 기다리는 동안). 리소스 제약이 지연을 일으키는 위치를 식별하는 데 사용하세요. - 웨어하우스 실행 시간 — 각
unique_id의QueryExecutedspan 기간의 합이에요. 컴파일·정적 분석 같은 v2 측 작업은 제외돼요. 이 메트릭을 사용해 텔레메트리를 웨어하우스 쿼리 이력과 비교하세요. 웨어하우스 시간이 가장 높은 노드 찾기(선택):
duckdb.sql("""
SELECT
attributes.unique_id,
SUM((end_time_unix_nano - start_time_unix_nano) / 1e6) AS warehouse_ms
FROM 'telemetry.parquet'
WHERE event_type LIKE '%QueryExecuted%'
GROUP BY attributes.unique_id
ORDER BY warehouse_ms DESC
LIMIT 10
""").show()
유형별 결과 집계:
duckdb.sql("""
SELECT
attributes.node_outcome,
COUNT(*) as count
FROM 'telemetry.parquet'
WHERE attributes.node_outcome IS NOT NULL
GROUP BY attributes.node_outcome
""").show()
웹 기반 Parquet 뷰어
로컬 설치 없이 임시 탐색을 위해 PondPilot 같은 웹 기반 Parquet 뷰어에서 Parquet 파일을 업로드하고 브라우저에 SQL 쿼리를 실행할 수 있어요. 일부 뷰어는 익숙하지 않은 스키마 탐색을 돕는 LLM 지원 쿼리 생성을 지원해요.
OpenTelemetry integration
dbt v2의 기본 OTLP 지원으로 Datadog, Jaeger, Google Cloud Trace, Grafana Tempo, Honeycomb을 포함한 OpenTelemetry 호환 수신기 어디로든 텔레메트리를 보낼 수 있어요. 이를 통해 다음이 가능해요:
- 기존 관찰성과의 통합 — 커스텀 통합이 필요 없어요.
- 실패나 느린 빌드에 알림을 트리거하는 커스텀 알림.
- dbt trace를 다운스트림 서비스와 연결하는 크로스 시스템 상관관계.
- 다른 인프라와 함께 dbt를 보는 중앙 집중식 모니터링.
OTLP export 설정
다음 예시는 OTLP export를 구성해요:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
dbtf build --export-to-otlp
Mapping to dbt v1 concepts
dbt v1의 구조화 로깅에 익숙하다면 dbt v2 텔레메트리가 어떻게 매핑되는지 보여드릴게요:
| dbt v1 | dbt v2 telemetry |
|---|---|
| invocation_id | trace_id (같은 값, 다른 형식) |
| run_results.json status | node_outcome + skip_reason 또는 test_outcome |
| Event code (예: Q001) | event_type |
| --log-format json | --log-format otel 또는 --otel-file-name |
노드 상태 매핑
| dbt v1 status | dbt v2 outcome |
|---|---|
| success | node_outcome: success |
| error | node_outcome: error |
| skipped | node_outcome: skipped, skip_reason: upstream |
| pass (tests) | node_outcome: success, test_outcome: passed |
| warn (tests) | node_outcome: success, test_outcome: warned |
| fail (tests) | node_outcome: success, test_outcome: failed |
dbt v1의 fail 상태가 dbt v2의 node_outcome: success로 매핑된다는 점에 주의하세요. dbt v2는 "테스트가 성공적으로 실행되고 데이터 문제를 찾았다"와 "테스트를 실행할 수 없었다"를 구분하기 때문이에요. 이 분리로 더 정확한 알림과 재시도 로직이 가능해져요.
dbt v2는 dbt State로 재사용된 노드에 대해 skip_reason: cached를 추가하는데, dbt v1에는 해당하는 것이 없어요.
State-aware orchestration → dbt State — dbt State는 모든 엔진·환경(dbt v1, dbt 플랫폼, dbt v2)에서 동작해요. 2026년 6월 1일 이전에 state-aware orchestration을 사용하고 있었다면 계속 사용할 수 있어요. 무료 dbt State 시험을 시작하면 표준 30일 기간을 넘어 연장돼요. 계정에 연장이 적용되지 않으면 계정 팀에 문의하세요. 시작 방법은 Migrate from state-aware orchestration을 참고하세요.
Record structure
각 텔레메트리 레코드는 봉투(envelope) 필드와 이벤트별 attributes를 포함해요:
{
"record_type": "SpanEnd",
"trace_id": "f9a0a9e64c924b878133363ba3515e50",
"span_id": "0000000000000036",
"span_name": "Node(model.project.customers)",
"parent_span_id": "0000000000000017",
"start_time_unix_nano": "1756139116981079652",
"end_time_unix_nano": "1756139117234567890",
"severity_text": "INFO",
"event_type": "v1.public.events.fusion.node.NodeEvaluated",
"attributes": {
"unique_id": "model.project.customers",
"phase": "Run",
"node_outcome": "success"
}
}
| Field | Description |
|---|---|
| record_type | SpanStart, SpanEnd, 또는 LogRecord |
| trace_id | 실행의 고유 식별자(invocation_id와 같은 데이터지만 OTEL 형식) |
| span_id / parent_span_id | span 계층 구조 재구성용 |
| event_type | 필터링·파싱용 유형 식별자 |
| attributes | 이벤트별 데이터(스키마는 이벤트 유형마다 다르지만, OTEL 규칙과 달리 안정적인 protobuf 스키마로 엄격히 뒷받침됨) |
Schema stability
dbt v1의 구조화 로깅과 달리 dbt v2 텔레메트리는 엄격한 호환성 보장을 갖춘 공개 protobuf 스키마로 뒷받침돼요:
- 추가 전용(Additive only) — 새 필드·이벤트 유형은 추가될 수 있지만, 기존 필드는 절대 제거되거나 변경되지 않아요.
- 전방 호환(Forward compatible) — 스키마가 발전해도 통합이 계속 동작해요. 이 덕분에 dbt v2 텔레메트리는 프로덕션 통합, 오케스트레이터, 장기 분석 파이프라인의 신뢰할 수 있는 기반이 돼요.
Official client library
dbt Labs는 공식 오픈소스 클라이언트 라이브러리를 제공해요. 성능을 위해 Rust로 구축되었으며 다음과 같이 제공돼요:
- 독립형 Rust crate와 CLI(
dbt-telemetry-export-cli). - Rust 코어를 감싼 완전히 타입된 Python 패키지 —
uv tool install로 설치 가능해요. 이 라이브러리는 텔레메트리 데이터에 타입 안전하고 전방 호환되는 접근을 제공해요. JSONL을 실시간으로 스트리밍하고 Parquet 파일을 쿼리하며, 스키마 변경이 코드를 깨지 않는다는 확신을 갖고 커스텀 통합을 구축할 수 있어요. 공개 릴리스는 가까운 시일 내에 발표될 예정이에요.
더 알아보기 (Learn more)
- Logs — 공유 CLI 로깅 구성
- Anonymous usage stats — 익명 사용 통계
- dbt State — 상태 인식 오케스트레이션