# Fusion telemetry and observability

The dbt Fusion engine provides a comprehensive observability system that replaces [dbt Core's structured logging](./events-logging.md#structured-logging). Built on [OpenTelemetry](https://opentelemetry.io/) conventions and backed by a stable protobuf schema, it enables deep integration with orchestrators, observability platforms, and custom tooling.

For shared CLI logging configs such as `--log-format` and `--log-level`, refer to [Logs](./global-configs/logs.md).

This system is separate from the anonymous usage statistics that dbt sends to dbt Labs. To configure anonymous usage statistics, refer to [Anonymous usage stats](./global-configs/usage-stats.md).

This uses the same integration that dbt platform relies on for orchestration and monitoring, providing proven and production-ready features that work at scale.

## Available output formats

Fusion telemetry supports three output formats, which you can enable independently:

| Format      | Use case                                                              | Availability                |
| ----------- | --------------------------------------------------------------------- | --------------------------- |
| **JSONL**   | Real-time monitoring, streaming to downstream systems.                | Written as events occur.    |
| **Parquet** | Post-run analysis, querying, and long-term storage.                   | Written when runs complete. |
| **OTLP**    | Integration with observability platforms (Datadog, Jaeger, and more). | Streamed in real-time.      |

### Enabling telemetry output

The following are some examples of options for enabling telemetry output (You can combine multiple outputs in a single run):

Write JSONL to a file (saves to the `logs/` directory):

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

Stream JSONL to stdout:

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

Write a Parquet file (saves to `target/metadata/` directory):

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

Write a Parquet file with full trace-level logs (recommended for debugging):

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

Export to an OpenTelemetry collector:

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

### Download telemetry from platform job runs

On the dbt platform, Fusion job runs store OTel telemetry as Parquet artifacts for dbt command steps. From a completed run, open the **Run summary** tab, select a step, and click **Download** > **Download OTel log**. The option appears only for Fusion runs where the step produced an OTel file. For step-by-step instructions, refer to [Downloading logs](../docs/deploy/run-visibility.md#access-logs).

#### Retrieve telemetry using the API

You can also retrieve the OTel Parquet artifact for a run step through the [dbt Administrative API v2](https://docs.getdbt.com/dbt-cloud/api-v2#/operations/Retrieve%20Run%20Artifact), which lets you download artifacts after a job completes. Use this to automate ingestion of node outcomes and test outcomes into a downstream system, such as a data quality framework in your warehouse.

Each Fusion command step that produces telemetry writes a `telemetry-STEP_NUMBER-otel.parquet` artifact. Some steps like `dbt deps` don't produce a Parquet artifact.

You can use the Retrieve Run Artifact endpoint to fetch this 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
```

Replace `YOUR_ACCESS_URL` with the [Access URL](../docs/platform/about-platform/access-regions-ip-addresses.md) for your region and plan, and `ACCOUNT_ID`, `RUN_ID`, `STEP_NUMBER` with your values. Authenticate with a [service account token](../docs/dbt-apis/service-tokens.md) or [personal access token](../docs/dbt-apis/user-tokens.md).

For example, you can do this with `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
```

To find which step produced the telemetry artifact you want, list the run's steps by including `run_steps` in the run details request:

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

You can only retrieve this artifact for Fusion steps that emitted an OTel log.

## Telemetry data

Fusion telemetry contains two types of records:

* **Spans** — Operations with a start and end time (like compiling a model or running a test).
* **Log records** — Point-in-time events within a span.

### Telemetry hierarchy

Every dbt command creates a hierarchy of spans:

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

The `trace_id` (also known as `invocation_id`) remains consistent across all telemetry records for a single dbt command, making it easy to correlate events.

## Node outcome

Every node produces a result for each phase it participates in. Some phases, such as `parse`, don't involve node-level execution, so they don't produce node spans or node outcomes.

The `node_outcome` field indicates whether or not Fusion executed the node's operation.

| Outcome    | Description                                                       |
| ---------- | ----------------------------------------------------------------- |
| `success`  | The node operation was completed with no errors.                  |
| `error`    | The node operation failed to execute (for example, syntax error). |
| `skipped`  | The node was not evaluated (see [skip reasons](#skip-reasons)).   |
| `canceled` | The node was interrupted (for example, user pressed Ctrl+C).      |

### Skip reasons

When Fusion skips a node, the telemetry includes a reason:

| Skip reason      | Description                                                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `upstream`       | A dependency failed.                                                                                                            |
| `cached`         | Fusion reused results from cache (no changes detected via [dbt State](../docs/deploy/dbt-state-about.md)). |
| `phase_disabled` | The phase was disabled (for example, `--static-analysis off`).                                                                  |
| `noop`           | Node doesn't perform work in this phase (for example, ephemeral models).                                                        |

### Test outcomes

When a test executes successfully (`node_outcome: success`), it reports the test result:

| Test outcome | Description                                    |
| ------------ | ---------------------------------------------- |
| `passed`     | No failures detected.                          |
| `warned`     | Failures detected, but configured as warnings. |
| `failed`     | Failures detected (data quality issue).        |

Test outcomes

A test with `node_outcome: success` and `test_outcome: failed` means Fusion successfully ran the test, and the test reported data quality issues. This differs from `node_outcome: error`, which means the test itself couldn't run (for example, invalid SQL).

## Querying telemetry data

Query the telemetry data to gain deeper insights into your dbt runs.

### JSONL examples

The following are some examples of querying the JSONL telemetry data.

Watch for errors in real-time:

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

List skipped nodes, reasons, and upstream details:

```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 }'
```

### Downloading telemetry from the dbt platform

If you ran a job in the dbt platform, you can download the OpenTelemetry (OTEL) Parquet artifact from the run page using the **Download** dropdown. The download includes the `telemetry-<step>-otel.parquet` file for each step in the run.

### Parquet analysis with DuckDB

Leverage DuckDB to better understand your telemetry data stored in Parquet files.

Find slowest nodes:

```python
import duckdb
duckdb.sql("""
    SELECT
        attributes.unique_id,
        (end_time_unix_nano - start_time_unix_nano) / 1e6 AS duration_ms
    FROM 'telemetry.parquet'
    WHERE event_type LIKE '%NodeProcessed%'
    ORDER BY duration_ms DESC
    LIMIT 10
""").show()
```

Count outcomes by type:

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

### Web-based Parquet viewers

For ad hoc exploration without a local install, web-based Parquet viewers (such as [PondPilot](https://app.pondpilot.io/)) let you upload a Parquet file and run SQL queries in the browser. Some viewers support LLM-assisted query generation to help you explore an unfamiliar schema.

## OpenTelemetry integration

Fusion's native OTLP support lets you send telemetry directly to any OpenTelemetry-compatible receiver, including Datadog, Jaeger, Google Cloud Trace, Grafana Tempo, and Honeycomb.

This enables:

* Integration with existing observability — No custom integrations needed.
* Custom alerts that trigger notifications on failures or slow builds.
* Correlate across systems that links dbt traces with downstream services.
* Centralized monitoring to view dbt alongside your other infrastructure.

### Setting up OTLP export

The following example configures the OTLP export:

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

## Mapping to dbt Core concepts

If you're familiar with dbt Core's structured logging, here's how Fusion telemetry maps:

| dbt Core                           | Fusion telemetry                                 |
| ---------------------------------- | ------------------------------------------------ |
| `invocation_id`                    | `trace_id` (same value, different format)        |
| `run_results.json` status          | `node_outcome` + `skip_reason` or `test_outcome` |
| Event `code` (for example, `Q001`) | `event_type`                                     |
| `--log-format json`                | `--log-format otel` or `--otel-file-name`        |

### Node status mapping

| dbt Core status | Fusion 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`  |

Note that dbt Core's `fail` status maps to Fusion's `node_outcome: success` because Fusion distinguishes between "the test ran successfully and found data issues" versus "the test couldn't run." This separation enables more precise alerting and retry logic.

Fusion adds `skip_reason: cached` for nodes reused via [State Aware Orchestration](../docs/deploy/state-aware-about.md), which has no dbt Core equivalent.

State-aware orchestration is now dbt State

[dbt State](../docs/deploy/dbt-state-about.md) works with all engines and environments: dbt Core, dbt platform, and Fusion

If you're using state-aware orchestration prior to June 1, 2026, you can continue using it. Your dbt State trial will be extended until the billing period begins on September 1, 2026. If your trial wasn't extended, contact your account team. To get started, refer to [Migrate from state-aware orchestration](../docs/deploy/dbt-state-migration.md).

## Record structure

Each telemetry record contains envelope fields plus event-specific `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"
  }
}
```

| Field                        | Description                                                                                                                       |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `record_type`                | `SpanStart`, `SpanEnd`, or `LogRecord`                                                                                            |
| `trace_id`                   | Unique identifier for the invocation (same data as `invocation_id` but in OTEL format).                                           |
| `span_id` / `parent_span_id` | For reconstructing the span hierarchy.                                                                                            |
| `event_type`                 | Type identifier for filtering and parsing.                                                                                        |
| `attributes`                 | Event-specific data (schema varies by event type, but unlike OTEL conventions, it's strictly backed by a stable protobuf schema). |

## Schema stability

Unlike dbt Core's structured logging, Fusion telemetry is backed by a public protobuf schema with strict compatibility guarantees:

* **Additive only** — New fields and event types may be added, but existing fields are never removed or changed.
* **Forward compatible** — Your integrations will continue to work as the schema evolves.

This makes Fusion telemetry a reliable foundation for production integrations, orchestrators, and long-term analytics pipelines.

## Official client library

dbt Labs provides an official open-source client library. Built in Rust for performance, it is available as:

* A standalone Rust crate and CLI (`dbt-telemetry-export-cli`).
* A fully typed Python package wrapping the Rust core, installable via `uv tool install`.

The library provides type-safe, forward-compatible access to telemetry data. Stream JSONL in real time, query Parquet files, and build custom integrations with confidence that schema changes won't break your code.

We'll be announcing the public release in the near future.

## Was this page helpful?

YesNo

[Privacy policy](https://www.getdbt.com/cloud/privacy-policy)[Create a GitHub issue](https://github.com/dbt-labs/docs.getdbt.com/issues)

This site is protected by reCAPTCHA and the Google [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms) apply.
