# Instrument FastAPI with OpenTelemetry

> Send traces, logs and metrics from a FastAPI app to Telemetry Machine with OpenTelemetry's zero-code Python instrumentation. No code changes.

OpenTelemetry's zero-code Python instrumentation traces every request, sends your `logging` records as logs, and records HTTP metrics. Your code doesn't change.

## Create the datasets

Each signal goes to its own dataset. Create three, and an ingest key that can write to all of them, with [tm-cli](https://telemetrymachine.com/docs/quick-start#tm-cli) or the [MCP server](https://telemetrymachine.com/docs/quick-start#option-1-set-up-with-a-coding-agent):

```console tab="tm-cli"
$ tm-cli datasets create --name shop-prod-traces --type traces
$ tm-cli datasets create --name shop-prod-logs --type logs
$ tm-cli datasets create --name shop-prod-metrics --type metrics
$ tm-cli api-keys create --name shop-api --dataset shop-prod-traces --dataset shop-prod-logs --dataset shop-prod-metrics
```

```json tab="MCP"
create_dataset { "name": "shop-prod-traces", "signal_type": "traces" }
create_dataset { "name": "shop-prod-logs", "signal_type": "logs" }
create_dataset { "name": "shop-prod-metrics", "signal_type": "metrics" }
create_ingest_key {
  "name": "shop-api",
  "datasets": ["shop-prod-traces", "shop-prod-logs", "shop-prod-metrics"]
}
```

tm-cli shows the key once, in `api_key.api_key`. Over MCP, your agent gets it once and saves it in your app's configuration. In the app, [create each dataset](https://telemetrymachine.com/datasets/new), then a key for all three under **API keys** in your organization's settings.

## Install

In your app's virtual environment, after your app's own packages:

```console
$ pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http
$ opentelemetry-bootstrap -a install
```

`opentelemetry-bootstrap` installs the instrumentation for each library it finds, such as FastAPI, `logging` and `httpx`.

## Configure

```bash
export TM_INGEST_KEY='<ingest-key>'
export OTEL_SERVICE_NAME=shop-api
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.telemetrymachine.com
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="X-API-Key=$TM_INGEST_KEY,X-Dataset-Name=shop-prod-traces"
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="X-API-Key=$TM_INGEST_KEY,X-Dataset-Name=shop-prod-logs"
export OTEL_EXPORTER_OTLP_METRICS_HEADERS="X-API-Key=$TM_INGEST_KEY,X-Dataset-Name=shop-prod-metrics"
```

## Run

Put `opentelemetry-instrument` in front of the command that starts your app:

```console
$ opentelemetry-instrument uvicorn main:app
```

It works the same with `fastapi run`, `--reload` and `--workers`.

## Check it arrived

Send your app a few requests, then open each explorer and choose the dataset:

- **Traces**, `shop-prod-traces`: one server span per request, named after its route, such as `GET /items/{item_id}`.
- **Logs**, `shop-prod-logs`: your `logging` records, each with its request's `trace.trace_id`.
- **Metrics**, `shop-prod-metrics`: `http.server.duration`, sent once a minute.

Or count spans by name with tm-cli or the MCP server:

```console tab="tm-cli"
$ tm-cli query shop-prod-traces --since 15m --select "name, count(*)" --group-by name
```

```json tab="MCP"
run_query {
  "dataset": "shop-prod-traces",
  "description": "Spans by name",
  "since": "15m",
  "select": "name, count(*)",
  "group_by": "name"
}
```

## Troubleshooting

- **`Requested component 'otlp_proto_grpc' not found`:** set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. The Python distro defaults to gRPC, which Telemetry Machine doesn't accept.
- **No `INFO` logs:** Python's root logger drops records below `WARNING`. Call `logging.basicConfig(level=logging.INFO)` at startup.
- **A library you added isn't traced:** run `opentelemetry-bootstrap -a install` again.

---

All pages: https://telemetrymachine.com/docs/llms.txt
