> ## Documentation Index
> Fetch the complete documentation index at: https://learn.kubit.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Recipe: try Kubit on your Pydantic Logfire traces

> Export traces from Pydantic Logfire with trace-export and upload them to a Kubit Sandbox workspace for a proof of concept, with no change to your app.

This recipe runs a proof of concept on traces you already have in Pydantic Logfire. You export them to a file with Kubit's open-source [trace-export](https://github.com/Kubit-AI/trace-export) tool, then upload the file to a Sandbox workspace. Your app, its instrumentation and Logfire stay as they are. It takes about 30 minutes.

A Sandbox workspace takes uploaded trace files only. It has no ingestion pipeline and no API key. To send live traces later, follow the [Quickstart](https://learn.kubit.ai/agent-observability/get-started/quickstart.md) in a new Agent workspace.

## Before you start

* [ ] A Logfire project with traces from your agent. Pydantic AI traces work best, because Pydantic AI already writes the OpenTelemetry GenAI attributes Kubit reads.
* [ ] Permission to create a read token in that Logfire project
* [ ] Python 3.10 or later, and git
* [ ] A Kubit account that can create workspaces. Sign-up needs an early-access link from Kubit or an invite from your organization. See [Set up your organization](https://learn.kubit.ai/start-here/admin.md).

## Prompt for your coding agent

Paste this into Claude Code, Cursor or Codex. It runs the export and leaves the token and the upload to you:

```text theme={null}
Read https://learn.kubit.ai/agent-observability/cookbook/logfire-sandbox.md.
Clone trace-export and install the Logfire exporter, as in the steps "Install trace-export" and "Export the traces".
I'll put LOGFIRE_READ_TOKEN in pydantic-logfire/.env myself. Don't print the token or ask me for it.
Export spans from <since> to <until> into data/logfire.otel.jsonl and show me the summary.
```

## Steps

### 1. Create a Logfire read token

In Logfire, open the project with your traces, then go to its settings and **Read tokens**. Create a token and copy it.

A read token can read one project only. trace-export works out the region (US or EU) from the token.

### 2. Install trace-export

Clone the repository and install the Logfire exporter in a virtual environment:

```bash theme={null}
git clone https://github.com/Kubit-AI/trace-export.git
cd trace-export
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r pydantic-logfire/requirements.txt
```

Then create the token file, readable only by you:

```bash theme={null}
cp pydantic-logfire/.env.example pydantic-logfire/.env
chmod 600 pydantic-logfire/.env
```

Paste the token after `LOGFIRE_READ_TOKEN=` and save the file. Keep the token out of the command line and out of source files.

### 3. Check the project with a sample

Export a 10-span sample to check that the token reads the right project:

```bash theme={null}
python pydantic-logfire/export_traces.py --all-time --limit 10 --output data/sample.otel.jsonl
```

The output names the Logfire organization and project, then the number of spans:

```text Output theme={null}
Logfire project: <organization>/<project> (https://logfire-eu.pydantic.dev)
Downloaded 10 span rows in total
Converted 10 spans from 10 rows; skipped 0 non-span and 0 invalid rows. Output: data/sample.otel.jsonl
```

Without `--all-time`, the sample only covers the last 24 hours. If the project had no traffic in that time, you get `Downloaded 0 span rows in total`.

### 4. Export the traces

Export every span in a time range to one file. Set `--until` a few minutes in the past, because Logfire stores a span only when it ends:

```bash theme={null}
python pydantic-logfire/export_traces.py --no-sample \
  --since 2026-10-01T00:00:00Z --until 2026-11-10T12:00:00Z \
  --output data/logfire.otel.jsonl --json-summary
```

The last line is a JSON summary. Check that `status` is `ok` and note `downloaded_rows`:

```text Output theme={null}
Converted 5334 spans from 5334 rows; skipped 0 non-span and 0 invalid rows. Output: data/logfire.otel.jsonl
{"version": "1.0.0", "source": "logfire", "status": "ok", "output": "data/logfire.otel.jsonl", "format": "otel", "output_written": true, "downloaded_rows": 5334, "sample": false, "limit": null, "since": "2026-10-01T00:00:00+00:00", "until": "2026-11-10T12:00:00+00:00", "query_timeout_seconds": 300, "warnings": [], "possibly_truncated": false}
```

| Option | What it does |
| - | - |
| `--since`, `--until` | Span start time from (inclusive) and to (exclusive), in UTC |
| `--environment NAME` | Only spans from that deployment environment. Repeat it for more than one. |
| `--limit N` | At most N spans. A limit or a time range can cut a trace in two. |
| `--all-time` | Every span your Logfire plan still keeps, instead of `--since` and `--until` |
| `--resume` | Continue an export that stopped. It needs `--keep-raw PATH` on the first run. |

The file has one span per line, as OTLP JSON. It keeps every span in the range, including HTTP, database and log spans, so Kubit can show the whole trace.

**Constraint:** The upload takes files up to **300 MB**. For a larger export, use a shorter time range or `--environment`.

### 5. Create a Sandbox workspace

In Kubit, open the workspace menu at the top left and click **New workspace**. Enter a **Workspace Name**, keep or change the **Workspace Timezone**, set **Origin** to **Sandbox** and click **Continue**.

![Create Workspace dialog with Workspace Name Logfire POC, Workspace Timezone Europe/Paris (UTC+1\*) and Origin Sandbox, with the hint that it uploads OTel trace files for quick testing with no pipeline, above the Continue button.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/create-sandbox-workspace.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=6c0906d90c8d284ed84ba7859eb15cac)

If **Sandbox** isn't in the **Origin** list, your organization doesn't have it turned on. Ask [support@kubit.co](mailto:support@kubit.co) to turn it on.

Kubit creates the workspace and opens **Modeler** on **Upload span data**.

### 6. Upload the file

Drag `data/logfire.otel.jsonl` onto **Drop a span file here**, or click **browse** and pick it. Then click **Upload & preview**.

![Upload span data panel in the Modeler. A note says the first upload sets the workspace's destination to the Kubit-managed warehouse. The drop zone reads Drop a span file here or browse, JSONL or JSON up to 300 MB, from OTLP, Langfuse, Phoenix, MLflow, LangSmith, Braintrust, Datadog, AgentCore or Cortex.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/upload-span-data.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=9e03c5947cd2304e42f986f5c5d9d431)

The first upload sets the workspace's destination to the Kubit-managed warehouse. You can't switch the workspace to another connection later, even if you discard the file.

The **Uploads** tab shows the progress in 4 steps. Keep the browser tab open until the upload finishes.

### 7. Review and import

When the upload shows `Uploaded. Ready to review.`, Kubit reads a sample from the start of the file and shows a preview below the list of uploads. Check that **Format** reads **OpenTelemetry (OTLP) (detected)** and that **Accepted spans** equals **Records sampled**.

![Preview of logfire.otel.jsonl, 55.6 MB, marked Preview ready. The file is kept until the next day. Format is OpenTelemetry (OTLP) (detected), and the tiles read Records sampled 131, Accepted spans 131, Bytes read 1 MB and Detected formats otlp-envelope · 131 records.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/upload-preview.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=4d7018400cc3094c63585679a79c58d6)

Further down, the preview shows:

| Section | What to check |
| - | - |
| The span table | The span names you know from Logfire, such as `invoke_agent <agent name>`, `chat <model>` and `execute_tool <tool name>` |
| **Labels in this file** | Logfire exports carry no labels, so the sample has none. Leave **Use these labels and scores** as it is. |
| **Proposed initial model** | The model the import creates, with **Fact table mapping**, **Derived columns** and **Trace structure**. You don't need to change anything. |

Under **After import**, keep **Run the Evaluator**, so Kubit labels the traces as soon as the import succeeds. Tick **I reviewed the sample and model preview.** and click **Import spans**.

![After import section with Run the Evaluator selected rather than I'll upload labels, the box I reviewed the sample and model preview ticked, and the Import spans and Discard buttons.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/import-spans.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=3f66eafcfa5fd597477ba453222482d9)

The import runs through **Checking the file**, **Reading spans**, **Saving to the warehouse** and **Preparing reports**. You can leave the page while it runs. When it's done, the upload shows **Succeeded** with the number of **Spans imported**, which should match `downloaded_rows` from the export. Below it, **Enrichment** shows `The Evaluator is labelling traces.`

![The upload logfire.otel.jsonl marked Succeeded, with tiles reading Spans imported 5,334, Duplicates 0, Skipped 0 and Time range Oct 30, 2026. Under Enrichment, The Evaluator is labelling traces, with Add labels file and Run again buttons.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/import-summary.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=0ba9d45fb6197d030fc71915583a0f29)

**Constraint:** Kubit keeps an uploaded file for 1 day. If you don't import it by then, upload it again.

### 8. Explore the traces

Open **Activity**. Keep **Subject** on **Trace** and **Time Range** on **Last 1 Day**, and click **Execute**. **Time Range** counts back from the last day in your data, shown next to it as **Reports through**, so older exports still show up.

![Activity traces grid with a Timeline of trace counts in 5-minute bars above the columns Timestamp, Name, Spans, Latency (Ms), Tokens, Model, Labels, Trace ID, User ID and Session ID. Each row is a trace named scenario \{scenario}, with its span count, latency, tokens and labels such as turn\_not\_advanced, low\_judge\_confidence, answered and repeated\_tool\_call. The footer reads 423 Traces, 1-50 shown.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/activity-traces.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=0c684cdd49289dc796f0eac7730a1058)

The footer counts the traces, such as `423 Traces`. **Labels** reads `Not analyzed yet` until the Evaluator has labelled that trace.

Click a trace to open it. The header shows its **Latency**, **Total cost**, **Tokens**, **User** and **Session**. The **Timeline** shows the agent runs, model calls and tool calls as Pydantic AI recorded them, along with the HTTP and database spans Logfire captured. Click a `chat` span to see its **Model**, token counts and **Input** and **Output** messages.

![An open trace named scenario \{scenario} with an OK status, Latency 10.48s, Tokens 14,168 to 569, no User and a Session. The Timeline lists invoke\_agent spans for two agents, chat gemini-3.5-flash-lite calls, an execute\_tool span and a SELECT database span. The Preview of the selected chat span shows Type chat, Model gemini-3.5-flash-lite, Prompt tokens 480, Completion tokens 12 and the start of the Input messages.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/activity-trace.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=ff694c2a1a36008c9bfca0c65699e559)

An empty **User** means the spans carry no user ID. When the spans carry no session ID, **Session** shows the trace ID. To fill both for live traces, set `user.id` and `session.id` in your app. See [Add user and session IDs](https://learn.kubit.ai/agent-observability/integrate/sdk-python.md#add-user-and-session-ids).

### 9. Review the labels

Open **Evaluator**. Under **Uploads**, your file shows `Running` while the Evaluator labels its conversations, then the time of the last run.

![Evaluator page titled Label your conversations. Under Uploads, logfire.otel.jsonl was imported at 6:50 PM and its last run reads Running, Labelling since 6:50 PM, next to a disabled Relabel button. Below, Questions lists the system questions Answer relevant, Task resolved and Right tools.](https://mintcdn.com/kubit/nip5DDQ8d0G82bAs/images/agent-observability/cookbook/logfire/evaluator.png?fit=max\&auto=format\&n=nip5DDQ8d0G82bAs\&q=85\&s=997015e5e2401bba3dd1cfe8e2ba3a88)

The Evaluator answers these system questions for each conversation:

| Question | What it asks |
| - | - |
| **Answer relevant** | Does the assistant's reply directly address what the user asked? |
| **Task resolved** | By the end of this conversation, had the assistant completed the task the user asked for? |
| **Right tools** | Were the tools the agent invoked the right ones for the user's request? |
| **Grounded in tool results** | Is every identifier, amount, status and completed action in the assistant's final reply supported by the tool results shown? |

To ask your own yes or no, choice or score questions, click **Add custom question**, then **Relabel** the upload. Relabelling asks every question again and replaces the labels. The Evaluator labels one upload at a time.

When the run ends, go back to **Activity** and click **Refresh**. The **Labels** column shows each trace's labels, such as `answered`, `turn_not_advanced` or `repeated_tool_call`, as in the screenshot in [Explore the traces](#explore-the-traces).

## Verify

The proof of concept is ready when all three are true:

* The upload in **Modeler** > **Uploads** shows **Succeeded**, and **Spans imported** equals `downloaded_rows` in the export summary.
* **Activity** lists as many traces as your Logfire project had in the exported range.
* A trace you open shows its agent runs, `chat` spans with a **Model** and token counts, and `execute_tool` spans.

## If it fails

| Symptom | Cause | Fix |
| - | - | - |
| `Downloaded 0 span rows in total` | No spans in the time range. A sample without bounds covers the last 24 hours only. | Add `--all-time`, or set `--since` and `--until` to a range with traffic |
| The `Logfire project:` line names the wrong project | The read token belongs to another project | Create a read token in the right project and replace it in `pydantic-logfire/.env` |
| Warnings that end in `gen_ai_messages_not_schema_compatible` | Some messages don't match the OpenTelemetry GenAI message format, or the app doesn't capture message content | Nothing to fix for the upload. To capture content, don't set `include_content=False` in `logfire.instrument_pydantic_ai()`. |
| Values such as session IDs read `[Scrubbed due to ...]` | Logfire scrubs values that look sensitive when it stores them | Change Logfire's scrubbing settings for new spans. Exported spans keep the scrubbed value. |
| The upload is over 300 MB | The time range is too long | Export a shorter range, or one environment with `--environment` |
| Some traces have missing parent spans | The time range or `--limit` cut a trace | Widen the time range, and drop `--limit` |

## Next steps

* [Quickstart](https://learn.kubit.ai/agent-observability/get-started/quickstart.md): send live traces to an Agent workspace.
* [Concepts](https://learn.kubit.ai/agent-observability/concepts.md): users, sessions, traces, spans and Polaris annotations.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.