# Телеметрия и наблюдаемость в dbt v2

dbt v2 предоставляет комплексную систему наблюдаемости, которая заменяет [структурированное журналирование dbt v1](https://docs.getdbt.tech/reference/events-logging.md#structured-logging). Эта система основана на соглашениях [OpenTelemetry](https://opentelemetry.io/) и стабильной схеме protobuf и обеспечивает глубокую интеграцию с оркестраторами, платформами наблюдаемости и пользовательскими инструментами.

Общие настройки журналирования CLI, такие как `--log-format` и `--log-level`, описаны в разделе [Журналы](https://docs.getdbt.tech/reference/global-configs/logs.md).

Эта система не связана с анонимной статистикой использования, которую dbt отправляет в dbt Labs. О настройке анонимной статистики использования см. раздел [Анонимная статистика использования](https://docs.getdbt.tech/reference/global-configs/usage-stats.md).

Здесь используется та же интеграция, на которую опирается dbt platform для оркестрации и мониторинга, что обеспечивает проверенные, готовые к промышленной эксплуатации возможности, работающие при больших масштабах.

## Доступные форматы вывода

Телеметрия dbt v2 поддерживает три формата вывода, которые можно включать независимо друг от друга:

| Формат      | Сценарий использования                                                   | Доступность                                 |
| ----------- | ------------------------------------------------------------------------ | ------------------------------------------- |
| **JSONL**   | Мониторинг в реальном времени, потоковая передача в последующие системы. | Записывается по мере возникновения событий. |
| **Parquet** | Анализ после выполнения, запросы и долгосрочное хранение.                | Записывается по завершении запусков.        |
| **OTLP**    | Интеграция с платформами наблюдаемости (Datadog, Jaeger и другими).      | Передаётся потоком в реальном времени.      |

### Включение вывода телеметрии

Ниже приведены примеры включения вывода телеметрии (в одном запуске можно объединить несколько вариантов вывода):

Запись JSONL в файл (сохраняется в каталоге `logs/`):

```bash
dbtf build --otel-file-name telemetry.jsonl
```

Потоковый вывод JSONL в stdout:

```bash
dbtf build --log-format otel
```

Запись файла Parquet (сохраняется в каталоге `target/metadata/`):

```bash
dbtf build --otel-parquet-file-name telemetry.parquet
```

Запись файла Parquet с полными журналами уровня trace (рекомендуется для отладки):

```bash
dbtf build --log-level-file trace --otel-parquet-file-name telemetry.parquet
```

Экспорт в коллектор OpenTelemetry:

```bash
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" dbtf build --export-to-otlp
```

### Скачивание телеметрии запусков заданий на платформе

На dbt platform запуски заданий dbt v2 сохраняют телеметрию OTel в виде артефактов Parquet для шагов с командами dbt. Откройте вкладку **Run summary** завершённого запуска, выберите шаг и нажмите **Download** > **Download OTel log**. Этот пункт появляется только для запусков dbt v2, в которых шаг создал файл OTel. Пошаговые инструкции приведены в разделе [Скачивание журналов](https://docs.getdbt.tech/docs/deploy/run-visibility.md#access-logs).

#### Получение телеметрии через API

Артефакт OTel в формате Parquet для шага запуска также можно получить через [dbt Administrative API v2](https://docs.getdbt.tech/dbt-cloud/api-v2#/operations/Retrieve%20Run%20Artifact), который позволяет скачивать артефакты после завершения задания. Используйте эту возможность, чтобы автоматизировать загрузку результатов выполнения узлов и тестов в последующую систему, например во фреймворк контроля качества данных в вашем хранилище.

Каждый шаг с командой dbt v2, который создаёт телеметрию, записывает артефакт `telemetry-STEP_NUMBER-otel.parquet`. Некоторые шаги, например `dbt deps`, не создают артефакт Parquet.

Для получения этого артефакта можно использовать конечную точку Retrieve Run Artifact:

```bash
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` на [URL доступа](https://docs.getdbt.tech/docs/platform/about-platform/access-regions-ip-addresses.md) для вашего региона и тарифного плана, а `ACCOUNT_ID`, `RUN_ID`, `STEP_NUMBER` — на свои значения. Для аутентификации используйте [токен служебной учётной записи](https://docs.getdbt.tech/docs/dbt-apis/service-tokens.md) или [персональный токен доступа](https://docs.getdbt.tech/docs/dbt-apis/user-tokens.md).

Например, это можно сделать с помощью `curl`:

```bash
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 YOUR_TOKEN' \
  --output telemetry-4-otel.parquet
```

Чтобы определить, какой шаг создал нужный артефакт телеметрии, получите список шагов запуска, включив `run_steps` в запрос сведений о запуске:

```bash
GET https://YOUR_ACCESS_URL/api/v2/accounts/ACCOUNT_ID/runs/RUN_ID/?include_related=["run_steps"]
```

Этот артефакт можно получить только для шагов dbt v2, которые сформировали журнал OTel.

## Данные телеметрии

Телеметрия dbt v2 содержит два типа записей:

* **Спаны** — операции со временем начала и окончания (например, компиляция модели или выполнение теста).
* **Записи журнала** — события в определённый момент времени внутри спана.

### Иерархия телеметрии

Каждая команда dbt создаёт иерархию спанов:

```text
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, что упрощает сопоставление событий.

## Результат выполнения узла

Каждый узел формирует результат для каждой фазы, в которой он участвует. Некоторые фазы, например `parse`, не предусматривают выполнение на уровне узлов, поэтому для них не создаются спаны узлов или результаты выполнения узлов.

Поле `node_outcome` указывает, выполнил ли dbt v2 операцию узла.

| Результат  | Описание                                                                    |
| ---------- | --------------------------------------------------------------------------- |
| `success`  | Операция узла завершена без ошибок.                                         |
| `error`    | Не удалось выполнить операцию узла (например, из-за синтаксической ошибки). |
| `skipped`  | Узел не обрабатывался (см. [причины пропуска](#skip-reasons)).              |
| `canceled` | Выполнение узла прервано (например, пользователь нажал Ctrl+C).             |

### Причины пропуска

Когда dbt v2 пропускает узел, в телеметрию включается причина:

| Причина пропуска | Описание                                                                                                                                                  |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `upstream`       | Ошибка при выполнении зависимости.                                                                                                                        |
| `cached`         | dbt v2 повторно использовал результаты из кеша (изменения не обнаружены с помощью [dbt State](https://docs.getdbt.tech/docs/deploy/dbt-state-about.md)). |
| `phase_disabled` | Фаза была отключена (например, с помощью `--static-analysis off`).                                                                                        |
| `noop`           | Узел не выполняет работу в этой фазе (например, эфемерные модели).                                                                                        |

### Результаты тестов

Когда тест успешно выполняется (`node_outcome: success`), он сообщает результат проверки:

| Результат теста | Описание                                                   |
| --------------- | ---------------------------------------------------------- |
| `passed`        | Нарушений не обнаружено.                                   |
| `warned`        | Обнаружены нарушения, но они настроены как предупреждения. |
| `failed`        | Обнаружены нарушения (проблема качества данных).           |

Результаты тестов

Тест с `node_outcome: success` и `test_outcome: failed` означает, что dbt v2 успешно выполнил тест и тест сообщил о проблемах качества данных. Это отличается от `node_outcome: error`, который означает, что сам тест не удалось выполнить (например, из-за некорректного SQL).

## Запросы к данным телеметрии

Выполняйте запросы к данным телеметрии, чтобы глубже анализировать запуски dbt.

### Примеры для JSONL

Ниже приведены примеры запросов к данным телеметрии в формате JSONL.

Отслеживание ошибок в реальном времени:

```bash
tail -f telemetry.jsonl | jq 'select(.severity_text == "ERROR")'
```

Вывод пропущенных узлов, причин пропуска и сведений о предшествующих узлах:

```bash
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 platform, артефакт OpenTelemetry (OTEL) в формате Parquet можно скачать со страницы запуска через раскрывающееся меню **Download**. В скачиваемые данные входит файл `telemetry-<step>-otel.parquet` для каждого шага запуска.

### Анализ Parquet с помощью DuckDB

Используйте DuckDB, чтобы лучше разобраться в данных телеметрии, хранящихся в файлах Parquet.

Поиск самых медленных узлов по **затратам времени на обработку** (а не по полному времени существования с учётом ожидания допуска по лимиту подключений):

```python
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`)** измеряет полное время от начала до конца спана, включая ожидание допуска по лимиту подключений. При сборках с полной загрузкой потоков эта метрика может показывать узлы с самым долгим ожиданием в очереди, а не с наибольшим объёмом обработки.
* **Время простоя (`attributes.idle_time_ms`)** измеряет, сколько времени узел провёл в ожидании, а не в активной обработке, например в ожидании предшествующего узла или свободных вычислительных ресурсов. Используйте эту метрику, чтобы определить, где ограничения ресурсов вызывают задержки.
* **Время выполнения в хранилище** — это сумма длительностей спанов `QueryExecuted` для каждого `unique_id`. Оно не включает работу на стороне v2, такую как компиляция и статический анализ. Используйте эту метрику для сравнения телеметрии с историей запросов в хранилище.

Поиск узлов с наибольшим временем выполнения в хранилище (необязательно):

```python
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()
```

Подсчёт результатов по типам:

```python
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

Для разового анализа без локальной установки веб-инструменты просмотра Parquet (например, [PondPilot](https://app.pondpilot.io/)) позволяют загрузить файл Parquet и выполнять SQL-запросы в браузере. Некоторые инструменты поддерживают генерацию запросов с помощью LLM, чтобы упростить изучение незнакомой схемы.

## Интеграция с OpenTelemetry

Встроенная поддержка OTLP в dbt v2 позволяет отправлять телеметрию напрямую в любой приёмник, совместимый с OpenTelemetry, включая Datadog, Jaeger, Google Cloud Trace, Grafana Tempo и Honeycomb.

Это обеспечивает:

* Интеграцию с существующей системой наблюдаемости — без разработки собственных интеграций.
* Настраиваемые оповещения, отправляющие уведомления при сбоях или медленных сборках.
* Сопоставление данных между системами, связывающее трассировки dbt с последующими сервисами.
* Централизованный мониторинг для наблюдения за dbt вместе с остальной инфраструктурой.

### Настройка экспорта OTLP

В следующем примере настраивается экспорт OTLP:

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
dbtf build --export-to-otlp
```

## Соответствие понятиям dbt v1

Если вы знакомы со структурированным журналированием dbt v1, ниже показано, как ему соответствует телеметрия dbt v2:

| dbt v1                            | Телеметрия dbt v2                                 |
| --------------------------------- | ------------------------------------------------- |
| `invocation_id`                   | `trace_id` (то же значение, другой формат)        |
| Статус в `run_results.json`       | `node_outcome` + `skip_reason` или `test_outcome` |
| `code` события (например, `Q001`) | `event_type`                                      |
| `--log-format json`               | `--log-format otel` или `--otel-file-name`        |

### Соответствие статусов узлов

| Статус dbt v1  | Результат dbt v2                                 |
| -------------- | ------------------------------------------------ |
| `success`      | `node_outcome: success`                          |
| `error`        | `node_outcome: error`                            |
| `skipped`      | `node_outcome: skipped`, `skip_reason: upstream` |
| `pass` (тесты) | `node_outcome: success`, `test_outcome: passed`  |
| `warn` (тесты) | `node_outcome: success`, `test_outcome: warned`  |
| `fail` (тесты) | `node_outcome: success`, `test_outcome: failed`  |

Обратите внимание: статус `fail` в dbt v1 соответствует `node_outcome: success` в dbt v2, поскольку dbt v2 различает ситуации «тест успешно выполнился и обнаружил проблемы с данными» и «тест не удалось выполнить». Такое разделение позволяет точнее настраивать оповещения и логику повторных попыток.

dbt v2 добавляет `skip_reason: cached` для узлов, результаты которых повторно используются через [dbt State](https://docs.getdbt.tech/docs/deploy/dbt-state-about.md); в dbt v1 аналога этому нет.

Оркестрация с учетом состояния теперь называется dbt State

[dbt State](https://docs.getdbt.tech/docs/deploy/dbt-state-about.md) работает со всеми движками и средами: dbt v1, dbt platform и dbt v2

Если вы использовали оркестрацию с учетом состояния до 1 июня 2026 года, вы можете продолжать ее использовать. Когда вы начнете бесплатный пробный период dbt State, он будет продлен сверх стандартных 30 дней. Если продление не применено к вашей учетной записи, обратитесь к команде по работе с вашим аккаунтом. Чтобы начать, ознакомьтесь с разделом [Переход с оркестрации с учетом состояния](https://docs.getdbt.tech/docs/deploy/dbt-state-migration.md).

## Структура записи

Каждая запись телеметрии содержит общие поля и специфичные для события `attributes`:

```json
{
  "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"
  }
}
```

| Поле                         | Описание                                                                                                                                           |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `record_type`                | `SpanStart`, `SpanEnd` или `LogRecord`                                                                                                             |
| `trace_id`                   | Уникальный идентификатор вызова (те же данные, что и в `invocation_id`, но в формате OTEL).                                                        |
| `span_id` / `parent_span_id` | Для восстановления иерархии спанов.                                                                                                                |
| `event_type`                 | Идентификатор типа для фильтрации и разбора.                                                                                                       |
| `attributes`                 | Данные, специфичные для события (схема зависит от типа события, но, в отличие от соглашений OTEL, строго определяется стабильной схемой protobuf). |

## Стабильность схемы

В отличие от структурированного журналирования dbt v1, телеметрия dbt v2 основана на общедоступной схеме protobuf со строгими гарантиями совместимости:

* **Только добавления** — могут добавляться новые поля и типы событий, но существующие поля никогда не удаляются и не изменяются.
* **Совместимость с будущими версиями** — ваши интеграции продолжат работать по мере развития схемы.

Это делает телеметрию dbt v2 надёжной основой для интеграций в промышленной эксплуатации, оркестраторов и долгосрочных аналитических конвейеров.

## Официальная клиентская библиотека

dbt Labs предоставляет официальную клиентскую библиотеку с открытым исходным кодом. Для высокой производительности она написана на Rust и доступна в виде:

* Отдельного крейта Rust и CLI (`dbt-telemetry-export-cli`).
* Полностью типизированного пакета Python, оборачивающего ядро на Rust и устанавливаемого с помощью `uv tool install`.

Библиотека предоставляет типобезопасный доступ к данным телеметрии с совместимостью с будущими версиями. Обрабатывайте поток JSONL в реальном времени, выполняйте запросы к файлам Parquet и создавайте собственные интеграции, будучи уверенными, что изменения схемы не нарушат работу вашего кода.

Мы объявим о публичном выпуске в ближайшее время.
