---
title: "Python"
description: "Configure Middleware APM for Python applications to monitor performance, detect bottlenecks, and track errors in real time."
section: "APM"
breadcrumb: "APM > Python > Middleware Agent"
canonical: "https://docs.middleware.io/get-started/apm/python"
---
# Python

Use the Middleware Python APM SDK to collect traces, metrics, logs and profiles from Python applications such as Django, Flask and FastAPI, with automatic instrumentation. The table shows which data types it collects.

| Traces | Metrics | App Logs | Custom Logs | Profiling |
| --- | --- | --- | --- | --- |
| ✅ | ✅ | ✅ | ✅ | ✅ |

## 1. Prerequisites

Before you instrument anything, make sure the basics are in place:

- Python 3.9+ and pip 23.1.2+ – verify your versions:

  ```bash
      python3 --version
  pip --version
  ```
- (Host mode only) If your app runs in a container, point it at the Host Agent using `MW_AGENT_SERVICE`.

  - Docker default bridge gateway: `172.17.0.1`
  - Kubernetes service name: `mw-service.mw-agent-ns.svc.cluster.local`
    (discover with `kubectl get service --all-namespaces | grep mw-service`).

## 2. Install

Create an isolated environment, install the SDK, then add auto-instrumentation shims for your frameworks:

```bash
    python -m venv .venv
source .venv/bin/activate

# SDK
pip install middleware-io

# (optional) enable continuous profiling features
pip install 'middleware-io[profiling]'

# Add OpenTelemetry auto-instrumentation libraries for installed packages (e.g., Flask)
middleware-bootstrap -a install
```

What `middleware-bootstrap -a install` does: it scans your active `site-packages` and installs the matching `opentelemetry-instrumentation-*` libraries for any frameworks you already have (e.g., Flask → `opentelemetry-instrumentation-flask`).

You can confirm with `pip list | grep -i flask`. For containers, add `RUN middleware-bootstrap -a install` to your Dockerfile.

## 3. Instrumentation

Pick Host (with the Middleware Host Agent) or Serverless (send directly to Middleware). Within each, choose Auto (zero-code) or Manual (call the tracker in code).

#### Host (with Middleware Host Agent)

#### Auto (zero-code)

Use the CLI wrapper that the SDK installs; it starts your app with the correct instrumentation:

```bash
export MW_SERVICE_NAME='MyPythonApp'
middleware-run python app.py
```

The SDK adds a `middleware-run` CLI to your venv; use it instead of raw commands like flask run so telemetry is captured. In Host mode, the OTLP target defaults to the local agent (`http://localhost:9319`).

#### Manual (in code)

Call `mw_tracker(...)` once at startup for more control (sampling, detectors, debug output, etc.):

```python
from middleware import mw_tracker, MWOptions

mw_tracker(MWOptions(
  service_name="MyPythonApp",
  console_exporter=True,   # echo telemetry to stdout (dev only)
  log_level="DEBUG",
))
```

Run it with:

```bash
export MW_TRACKER=True
middleware-run python app.py
```

`MW_TRACKER=True` is required when you instrument using `mw_tracker()`.

#### Serverless (no Host Agent)

#### Auto (zero-code)

Set the tenant endpoint and API key, then start with the same runner:

```bash
export MW_API_KEY='your-initial-token'
export MW_TARGET='your-initial-uid'
export MW_SERVICE_NAME='MyPythonApp'
middleware-run python app.py
```

In serverless mode the app exports directly to Middleware, so both `MW_API_KEY` and `MW_TARGET` are required. [(docs.middleware.io)](https://docs.middleware.io/get-started/apm/python)

#### Manual (in code)

Supply the target and token via `MWOptions`:

```python
from middleware import mw_tracker, MWOptions, DETECT_AWS_EC2

mw_tracker(MWOptions(
  access_token="your-initial-token",
  target="your-initial-uid",
  service_name="MyPythonApp",
  detectors=[DETECT_AWS_EC2],     # example
  otel_propagators="b3,tracecontext",
  console_exporter=True,
  log_level="DEBUG",
))
```

Run it with:

```bash
export MW_TRACKER=True
middleware-run python app.py
```

## 4. Advanced configuration (optional)

#### Service name

Two ways to set your service name:

```bash
export MW_SERVICE_NAME='MyPythonApp'
```

Or set `service_name` in `MWOptions(...)`.

#### Continuous profiling

Enable continuous profiling:

```bash
pip install 'middleware-io[profiling]'
export MW_APM_COLLECT_PROFILING=True
# serverless requires MW_API_KEY + MW_TARGET as above
```

#### Framework run commands

Pick the run command that matches your server/framework.

#### Django

```bash
export DJANGO_SETTINGS_MODULE='mysite.settings'
middleware-run python manage.py runserver
```

#### Gunicorn

```bash
export MW_API_KEY='your-initial-token'
export MW_TARGET='your-initial-uid'
export DJANGO_SETTINGS_MODULE='demo.settings'
middleware-run gunicorn -c conf/gunicorn.conf.py --workers=4 --bind 0.0.0.0:8000 --timeout 120 demo.wsgi
```

#### Uvicorn (FastAPI/Starlette)

```bash
export MW_API_KEY='your-initial-token'
export MW_TARGET='your-initial-uid'
middleware-run uvicorn main:app --host localhost --port 5002
```

> **Note:** If you’re running in a container with Host mode, set `MW_AGENT_SERVICE` so your app can reach the Host Agent:
>
> - Docker default gateway: `172.17.0.1`
> - Kubernetes service: `mw-service.mw-agent-ns.svc.cluster.local`

What “Zero-code” actually does: automatic patching via monkey-patching of popular libraries at runtime (e.g., Flask), driven by the instrumentation packages you installed with `middleware-bootstrap`.

## 5. Sending custom data

#### Custom metrics

Create and use a meter to send custom metrics:

```python
from opentelemetry.metrics import get_meter_provider

meter = get_meter_provider().get_meter("custom_meter")

request_counter = meter.create_counter(
  "request_counter", description="Counts the number of requests"
)

request_counter.add(1, {"endpoint": "/home"})
```

#### Custom traces

Create and use a tracer to send custom spans:

```python
from opentelemetry.trace import get_tracer

tracer = get_tracer("custom_tracer")

with tracer.start_as_current_span("custom_span"):
  print("Doing some work within the span")
```

#### Custom logs

Use `logging` to emit logs. If your app already has a logger wrapper, add these calls there:

```python
import logging

logging.info("info sample")
logging.warning("Sample Warning Log")
logging.error("Sample Error Log.", extra={"tester": "Alex"})
```

#### Custom attributes

Attach attributes to spans (they become filter/group dimensions in Middleware):

```python
from opentelemetry.trace import get_tracer

tracer = get_tracer("custom_tracer")

with tracer.start_as_current_span("span_with_attributes") as span:
  span.set_attribute("user.email", "johndoe@gmail.com")
  span.set_attribute("user.id", 1234)
```

#### Stack traces

Record an exception and its stack trace:

```python
from middleware import record_exception

try:
  print("Divide by zero:", 1 / 0)
except Exception as e:
  record_exception(e)
```

## 6. Environment variables

OTel env vars are supported and take the highest priority if present. Then Middleware env vars, then code options.

| Config attribute | Environment variable(s) | Description/default | Example |
| --- | --- | --- | --- |
| `access_token` | `MW_API_KEY` | Auth token (required for serverless/direct). | `xxxxxxxx…` |
| `service_name` | `MW_SERVICE_NAME, OTEL_SERVICE_NAME` | Service name shown in APM. | `payments-api` |
| `collect_traces` | `MW_APM_COLLECT_TRACES` | Enable traces (default true). | `true` |
| `collect_metrics` | `MW_APM_COLLECT_METRICS` | Enable metrics (default true). | `true` |
| `collect_logs` | `MW_APM_COLLECT_LOGS` | Enable logs (default true). | `true` |
| `collect_profiling` | `MW_APM_COLLECT_PROFILING` | Enable profiling (requires profiling extra). | `true` |
| `log_level` | `MW_LOG_LEVEL, OTEL_LOG_LEVEL` | Logging level (default INFO). | `DEBUG` |
| `mw_agent_service` | `MW_AGENT_SERVICE` | Host Agent address in containers. | `172.17.0.1 / mw-service.mw-agent-ns...` |
| `target` | `MW_TARGET, OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint (Host default: http://localhost:9319). | `https://<MW_UID>.middleware.io:443` |
| `custom_resource_attributes` | `MW_CUSTOM_RESOURCE_ATTRIBUTES` | Comma-sep `k=v` list. | `call_id=123,region=us-east-1` |
| `otel_propagators` | `MW_PROPAGATORS, OTEL_PROPAGATORS` | Context propagation (default b3). | `b3,tracecontext` |
| `console_exporter` | `MW_CONSOLE_EXPORTER` | Echo telemetry to console (dev). | `true` |
| `debug_log_file` | `MW_DEBUG_LOG_FILE` | Log telemetry to files (with console exporter). | `true` |
| `project_name` | `MW_PROJECT_NAME` | Logical app/project name. | `ShopApp` |
| `sample_rate` | `MW_SAMPLE_RATE` | 0..1 (AlwaysOn=1, AlwaysOff=0). | `0.5` |
| `detectors` | `MW_DETECTORS` | e.g., `aws_lambda,gcp,azure,envvars`. | `aws_lambda,gcp` |

## 7. View your data

After you start the app, give it 3–5 minutes, then open [APM → Traces](https://app.middleware.io/apm/traces), Logs, and [APM → Continuous Profiling](https://app.middleware.io/apm/profiling) in Middleware. See “Application Instrumentation” for where to find dashboards, trace viewer, Log Explorer, and Alerts.

## 8. Troubleshooting

- No data (Host mode): Ensure you’re using `middleware-run`, the Host Agent is installed, and (in containers) `MW_AGENT_SERVICE` is set to the correct address/service.
- No data (Serverless): Make sure both `MW_API_KEY` and `MW_TARGET` are exported before starting.
- Instrumentation didn’t hook: Re-run `middleware-bootstrap -a install` after adding frameworks, and verify with `pip list` that `opentelemetry-instrumentation-*` packages exist.
- Need to confirm `register`/init ran? Temporarily set `MW_CONSOLE_EXPORTER=true` (and `MW_DEBUG_LOG_FILE=true`) to echo spans/logs to stdout/files while you test.
- Kubernetes reachability: find the service, then set `MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local`.

## OpenTelemetry Python

Use this option if you want upstream OpenTelemetry auto-instrumentation (`opentelemetry-distro`, `opentelemetry-instrument`) and OTLP export directly to Middleware.

- Full guide: [Python (OpenTelemetry)](/get-started/apm/python/otel)

Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us page or join our Slack channel.
