---
title: "Span Lifecycle"
description: "Learn how to add attributes to spans in Sentry to monitor performance and debug applications."
url: https://docs.sentry.io/platforms/python/tracing/span-lifecycle/
---

# Span Lifecycle | Sentry for Python

To capture transactions and spans customized to your organization's needs, you must first [set up tracing.](https://docs.sentry.io/platforms/python/tracing.md)

To add custom performance data to your application, you need to add custom instrumentation in the form of [spans](https://docs.sentry.io/concepts/key-terms/tracing/distributed-tracing.md#traces-transactions-and-spans). Spans are a way to measure the time it takes for a specific action to occur. For example, you can create a span to measure the time it takes for a function to execute.

## [Span Lifecycle](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#span-lifecycle)

In Python, spans are typically created using a context manager, which automatically manages the span's lifecycle. When you create a span using a context manager, the span automatically starts when entering the context and ends when exiting it. This is the recommended approach for most scenarios.

```python
import sentry_sdk

# Start a span for a task
with sentry_sdk.start_span(op="task", name="Create User"):
    # Your code here
    # The span will automatically end when exiting this block
    user = create_user(email="user@example.com")
    send_welcome_email(user)
    # The span automatically ends here when the 'with' block exits
```

You can call the context manager's `__enter__` and `__exit__` methods to more explicitly control the span's lifecycle.

## [Span Context and Nesting](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#span-context-and-nesting)

When you create a span, it becomes the child of the current active span. This allows you to build a hierarchy of spans that represent the execution path of your application:

```python
import sentry_sdk

with sentry_sdk.start_span(op="process", name="Process Data"):
    # This code is tracked in the "Process Data" span

    with sentry_sdk.start_span(op="task", name="Validate Input"):
        # This is now a child span of "Process Data"
        validate_data()

    with sentry_sdk.start_span(op="task", name="Transform Data"):
        # Another child span
        transform_data()
```

## [Span Starting Options](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#span-starting-options)

The following options can be used when creating spans:

| Option            | Type             | Description                 |
| ----------------- | ---------------- | --------------------------- |
| `op`              | `string`         | The operation of the span.  |
| `name`            | `string`         | The name of the span.       |
| `start_timestamp` | `datetime/float` | The start time of the span. |

## [Using the Context Manager](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#using-the-context-manager)

For most scenarios, we recommend using the context manager approach with `sentry_sdk.start_span()`. This creates a new span that automatically starts when entering the context and ends when exiting it.

```python
import sentry_sdk

with sentry_sdk.start_span(op="db", name="Query Users") as span:
    # Perform a database query
    users = db.query("SELECT * FROM users")

    # You can set data on the span
    span.set_data("user_count", len(users))
```

The context manager also correctly handles exceptions, marking the span as failed if an exception occurs:

```python
import sentry_sdk

try:
    with sentry_sdk.start_span(op="http", name="Call External API"):
        # If this raises an exception, the span will be marked as failed
        response = requests.get("https://api.example.com/data")
        response.raise_for_status()
except Exception:
    # The span is already marked as failed and has ended
    pass
```

## [Getting the Current Span](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#getting-the-current-span)

You can access the currently active span using `sentry_sdk.get_current_span()`:

```python
import sentry_sdk

# Get the current active span
current_span = sentry_sdk.get_current_span()
if current_span:
    current_span.set_data("key", "value")
```

## [Working with Transactions](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#working-with-transactions)

[Transactions](https://docs.sentry.io/product/insights/overview/transaction-summary.md#what-is-a-transaction) are a special type of span that represent a complete operation in your application, such as a web request. You can create transactions explicitly:

```python
import sentry_sdk

with sentry_sdk.start_transaction(name="Background Task", op="task") as transaction:
    # Your code here

    # You can add child spans to the transaction
    with sentry_sdk.start_span(op="subtask", name="Data Processing"):
        # Process data
        pass
```

## [Improving Span Data](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#improving-span-data)

### [Adding Span Attributes](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#adding-span-attributes)

Span attributes customize information you can get through tracing. This information can be found in the traces views in Sentry, once you drill into a span. You can capture additional context with span attributes. These can be key-value pairs of various Python types.

```python
import sentry_sdk

with sentry_sdk.start_span(op="db", name="Query Users") as span:
    # Execute the query
    users = db.query("SELECT * FROM users WHERE active = true")

    # You can add more data during execution
    span.set_data("result_count", len(users))
```

You can also add attributes to an existing span:

```python
import sentry_sdk

# Get the current span
span = sentry_sdk.get_current_span()
if span:
    # Set individual data points
    span.set_data("user_id", user.id)
    span.set_data("request_size", len(request.body))
```

### [Adding Attributes to All Spans](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#adding-attributes-to-all-spans)

To add attributes to all spans, use the `before_send_transaction` callback:

```python
import sentry_sdk
from sentry_sdk.types import Event, Hint

def before_send_transaction(event: Event, hint: Hint) -> Event | None:
    # Add attributes to the root span (transaction)
    if "trace" in event.get("contexts", {}):
        if "data" not in event["contexts"]["trace"]:
            event["contexts"]["trace"]["data"] = {}

        event["contexts"]["trace"]["data"].update({
            "app_version": "1.2.3",
            "environment_region": "us-west-2"
        })

    # Add attributes to all child spans
    for span in event.get("spans", []):
        if "data" not in span:
            span["data"] = {}

        span["data"].update({
            "component_version": "2.0.0",
            "deployment_stage": "production"
        })

    return event

sentry_sdk.init(
    # ...
    before_send_transaction=before_send_transaction
)
```

### [Adding Span Operations ("op")](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#adding-span-operations-op)

Spans can have an operation associated with them, which helps Sentry understand the context of the span. For example, database related spans have the `db` operation, while HTTP requests use `http.client`.

Sentry maintains a [list of well-known span operations](https://develop.sentry.dev/sdk/performance/span-operations/#list-of-operations) that you should use when applicable:

```python
import sentry_sdk

# HTTP client operation
with sentry_sdk.start_span(op="http.client", name="Fetch User Data"):
    response = requests.get("https://api.example.com/users")

# Database operation
with sentry_sdk.start_span(op="db", name="Save User"):
    db.execute(
        "INSERT INTO users (name, email) VALUES (%s, %s)",
        (user.name, user.email),
    )

# File I/O operation
with sentry_sdk.start_span(op="file.read", name="Read Config"):
    with open("config.json", "r") as f:
        config = json.load(f)
```

### [Updating the Span Status](https://docs.sentry.io/platforms/python/tracing/span-lifecycle.md#updating-the-span-status)

You can update the status of a span to indicate whether it succeeded or failed:

```python
import sentry_sdk

with sentry_sdk.start_span(op="task", name="Process Payment") as span:
    try:
        result = process_payment(payment_id)
        if result.success:
            # Mark the span as successful
            span.set_status("ok")
        else:
            # Mark the span as failed
            span.set_status("error")
            span.set_data("error_reason", result.error)
    except Exception:
        # Span will automatically be marked as failed when an exception occurs
        raise
```
