Skip to content

feat(python): SDK 3.x version of the Span Lifecycle page #14447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/platforms/python/tracing/span-lifecycle/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ with sentry_sdk.start_span(op="http.client", name="Fetch User Data"):

# Database operation
with sentry_sdk.start_span(op="db", name="Save User"):
db.execute(
db.execute(
"INSERT INTO users (name, email) VALUES (%s, %s)",
(user.name, user.email),
)
Expand Down
247 changes: 247 additions & 0 deletions docs/platforms/python/tracing/span-lifecycle/index__v3.x.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
---
title: Span Lifecycle
description: "Learn how to add attributes to spans in Sentry to monitor performance and debug applications."
sidebar_order: 10
---

<Alert>

To capture transactions and spans customized to your organization's needs, you must first <PlatformLink to="/tracing/">set up tracing.</PlatformLink>

</Alert>

To add custom performance data to your application, you need to add custom instrumentation in the form of [spans](/concepts/key-terms/tracing/distributed-tracing/#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.

<PlatformContent includePath="enriching-events/import" />

## 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"):
# 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

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

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

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 an attribute on the span
span.set_attribute("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

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_attribute("key", "value")
```

## Working with Transactions

[Transactions](/product/insights/overview/transaction-summary/#what-is-a-transaction), also known as root spans, are a special type of span that represent a complete operation in your application, such as a web request. A top-level span (without any parent spans in the current service) will automatically become a transaction:

```python
import sentry_sdk

with sentry_sdk.start_span(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
```

If you want to prevent a span from becoming a transaction, you can use
the `only_as_child_span` argument:

```python
import sentry_sdk

# This span will never be promoted to a transaction, but if there is a running
# span when this span starts, it'll be attached to it as a child span
with sentry_sdk.start_span(name="Background Task", op="task", only_as_child_span=True):
# Your code here
```

## Improving Span Data

### 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 are key-value pairs where the keys are non-empty strings and the values are either primitive Python types (excluding `None`), or a list of a single primitive Python type.

```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_attribute("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_attribute("user_id", user.id)
span.set_attribute("request_size", len(request.body))
```

### 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")

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

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_attribute("error_reason", str(result.error))
except Exception:
# Span will automatically be marked as failed when an exception occurs
raise
```
Loading