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

# Tracing instrumentation

> Instrument your Python application with OpenTelemetry for tracing in Apitally.

The Apitally SDK captures [OpenTelemetry](https://opentelemetry.io/docs/languages/python/) spans during request handling. This allows you to see exactly what happened during each request, including database queries, HTTP calls to external services, and custom operations.

If you have an existing OpenTelemetry setup, the SDK automatically registers itself as a span processor with the global `TracerProvider`. Otherwise, it creates its own. Spans created within a captured request using the standard OpenTelemetry API or instrumentation libraries will be captured.

## Instrument libraries

The SDK provides helper functions in `apitally.otel` to instrument popular libraries. These are thin wrappers around the official OpenTelemetry instrumentation packages, which need to be installed separately.

| Library                | Function                  | Required package                           |
| :--------------------- | :------------------------ | :----------------------------------------- |
| httpx                  | `instrument_httpx()`      | `opentelemetry-instrumentation-httpx`      |
| requests               | `instrument_requests()`   | `opentelemetry-instrumentation-requests`   |
| SQLAlchemy             | `instrument_sqlalchemy()` | `opentelemetry-instrumentation-sqlalchemy` |
| psycopg 3              | `instrument_psycopg()`    | `opentelemetry-instrumentation-psycopg`    |
| psycopg2               | `instrument_psycopg2()`   | `opentelemetry-instrumentation-psycopg2`   |
| asyncpg                | `instrument_asyncpg()`    | `opentelemetry-instrumentation-asyncpg`    |
| mysql-connector-python | `instrument_mysql()`      | `opentelemetry-instrumentation-mysql`      |
| redis-py               | `instrument_redis()`      | `opentelemetry-instrumentation-redis`      |
| PyMongo                | `instrument_pymongo()`    | `opentelemetry-instrumentation-pymongo`    |
| botocore (AWS SDK)     | `instrument_botocore()`   | `opentelemetry-instrumentation-botocore`   |

To get started, first install the required packages for the libraries you want to instrument. For example, to instrument `httpx` and `sqlalchemy`:

<CodeGroup>
  ```shell pip theme={null}
  pip install opentelemetry-instrumentation-httpx opentelemetry-instrumentation-sqlalchemy
  ```

  ```shell uv theme={null}
  uv add opentelemetry-instrumentation-httpx opentelemetry-instrumentation-sqlalchemy
  ```

  ```shell poetry theme={null}
  poetry add opentelemetry-instrumentation-httpx opentelemetry-instrumentation-sqlalchemy
  ```
</CodeGroup>

Then call the instrumentation functions at application startup.  Some functions accept an optional client, connection or engine argument to instrument a specific instance instead of all instances globally.

```python theme={null}
from apitally.otel import instrument_httpx, instrument_sqlalchemy

instrument_httpx()
instrument_sqlalchemy(engine)
```

## Create custom spans

For custom operations that aren't covered by library instrumentation, you can create spans manually using the helpers provided by the SDK. These are thin wrappers around the standard OpenTelemetry API, which you can also use directly.

### `@instrument` decorator

Use the `@instrument` decorator to automatically create a span for a function. It works with both sync and async functions:

```python theme={null}
from apitally.otel import instrument

@instrument
def process_order(order_id: int) -> None:
    ...
```

### `span()` context manager

Use the `span()` context manager to trace code blocks within a function:

```python theme={null}
from apitally.otel import span

def checkout(cart_id: int) -> None:
    with span("validate_cart") as s:
        s.set_attribute("cart.id", cart_id)
        # Validation logic here
        ...

    with span("process_payment"):
        # Payment logic here
        ...
```

## Capture handled exceptions

Apitally includes exceptions in request traces to help you diagnose server errors. Exceptions that propagate through your web framework are captured automatically.

If your application catches an exception and handles it without re-raising it, call `apitally.capture_exception()` to include it in the current request trace.

```python theme={null}
import apitally

try:
    process_order()
except Exception as exc:
    apitally.capture_exception(exc)
    # Return an error response without re-raising the exception
```
