Skip to content

Commit 4235d30

Browse files
committed
feat(python): SDK 3.x update for Span Lifecycle page
1 parent c1d5492 commit 4235d30

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
---
2+
title: Span Lifecycle
3+
description: "Learn how to add attributes to spans in Sentry to monitor performance and debug applications."
4+
sidebar_order: 10
5+
---
6+
7+
<Alert>
8+
9+
To capture transactions and spans customized to your organization's needs, you must first <PlatformLink to="/tracing/">set up tracing.</PlatformLink>
10+
11+
</Alert>
12+
13+
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.
14+
15+
<PlatformContent includePath="enriching-events/import" />
16+
17+
## Span Lifecycle
18+
19+
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.
20+
21+
```python
22+
import sentry_sdk
23+
24+
# Start a span for a task
25+
with sentry_sdk.start_span(op="task", name="Create User"):
26+
# Your code here
27+
# The span will automatically end when exiting this block
28+
user = create_user(email="[email protected]")
29+
send_welcome_email(user)
30+
# The span automatically ends here when the 'with' block exits
31+
```
32+
33+
You can call the context manager's `__enter__` and `__exit__` methods to more explicitly control the span's lifecycle.
34+
35+
## Span Context and Nesting
36+
37+
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:
38+
39+
```python
40+
import sentry_sdk
41+
42+
with sentry_sdk.start_span(op="process", name="Process Data"):
43+
# This code is tracked in the "Process Data" span
44+
45+
with sentry_sdk.start_span(op="task", name="Validate Input"):
46+
# This is now a child span of "Process Data"
47+
validate_data()
48+
49+
with sentry_sdk.start_span(op="task", name="Transform Data"):
50+
# Another child span
51+
transform_data()
52+
```
53+
54+
## Span Starting Options
55+
56+
The following options can be used when creating spans:
57+
58+
| Option | Type | Description |
59+
| ------------- | --------------- | ----------------------------------------------- |
60+
| `op` | `string` | The operation of the span. |
61+
| `name` | `string` | The name of the span. |
62+
| `start_timestamp` | `datetime/float`| The start time of the span. |
63+
64+
## Using the Context Manager
65+
66+
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.
67+
68+
```python
69+
import sentry_sdk
70+
71+
with sentry_sdk.start_span(op="db", name="Query Users") as span:
72+
# Perform a database query
73+
users = db.query("SELECT * FROM users")
74+
75+
# You can set an attribute on the span
76+
span.set_attribute("user_count", len(users))
77+
```
78+
79+
The context manager also correctly handles exceptions, marking the span as failed if an exception occurs:
80+
81+
```python
82+
import sentry_sdk
83+
84+
try:
85+
with sentry_sdk.start_span(op="http", name="Call External API"):
86+
# If this raises an exception, the span will be marked as failed
87+
response = requests.get("https://api.example.com/data")
88+
response.raise_for_status()
89+
except Exception:
90+
# The span is already marked as failed and has ended
91+
pass
92+
```
93+
94+
## Getting the Current Span
95+
96+
You can access the currently active span using `sentry_sdk.get_current_span()`:
97+
98+
```python
99+
import sentry_sdk
100+
101+
# Get the current active span
102+
current_span = sentry_sdk.get_current_span()
103+
if current_span:
104+
current_span.set_attribute("key", "value")
105+
```
106+
107+
## Working with Transactions
108+
109+
[Transactions](/product/insights/overview/transaction-summary/#what-is-a-transaction), also known as root spans, are a special type of spans 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:
110+
111+
```python
112+
import sentry_sdk
113+
114+
with sentry_sdk.start_span(name="Background Task", op="task") as transaction:
115+
# Your code here
116+
117+
# You can add child spans to the transaction
118+
with sentry_sdk.start_span(op="subtask", name="Data Processing"):
119+
# Process data
120+
pass
121+
```
122+
123+
If you want to prevent a span from becoming a transaction, you can use
124+
the `only_as_child_span` argument:
125+
126+
```python
127+
import sentry_sdk
128+
129+
# This span will never be promoted to a transaction, but if there is a running
130+
# span when this span starts, it'll be attached to it as a child span
131+
with sentry_sdk.start_span(name="Background Task", op="task", only_as_child_span=True):
132+
# Your code here
133+
```
134+
135+
## Improving Span Data
136+
137+
### Adding Span Attributes
138+
139+
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.
140+
141+
```python
142+
import sentry_sdk
143+
144+
with sentry_sdk.start_span(op="db", name="Query Users") as span:
145+
# Execute the query
146+
users = db.query("SELECT * FROM users WHERE active = true")
147+
148+
# You can add more data during execution
149+
span.set_data("result_count", len(users))
150+
```
151+
152+
You can also add attributes to an existing span:
153+
154+
```python
155+
import sentry_sdk
156+
157+
# Get the current span
158+
span = sentry_sdk.get_current_span()
159+
if span:
160+
# Set individual data points
161+
span.set_data("user_id", user.id)
162+
span.set_data("request_size", len(request.body))
163+
```
164+
165+
### Adding Attributes to All Spans
166+
167+
To add attributes to all spans, use the `before_send_transaction` callback:
168+
169+
```python
170+
import sentry_sdk
171+
from sentry_sdk.types import Event, Hint
172+
173+
def before_send_transaction(event: Event, hint: Hint) -> Event | None:
174+
# Add attributes to the root span (transaction)
175+
if "trace" in event.get("contexts", {}):
176+
if "data" not in event["contexts"]["trace"]:
177+
event["contexts"]["trace"]["data"] = {}
178+
179+
event["contexts"]["trace"]["data"].update({
180+
"app_version": "1.2.3",
181+
"environment_region": "us-west-2"
182+
})
183+
184+
# Add attributes to all child spans
185+
for span in event.get("spans", []):
186+
if "data" not in span:
187+
span["data"] = {}
188+
189+
span["data"].update({
190+
"component_version": "2.0.0",
191+
"deployment_stage": "production"
192+
})
193+
194+
return event
195+
196+
sentry_sdk.init(
197+
# ...
198+
before_send_transaction=before_send_transaction
199+
)
200+
```
201+
202+
### Adding Span Operations ("op")
203+
204+
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`.
205+
206+
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:
207+
208+
```python
209+
import sentry_sdk
210+
211+
# HTTP client operation
212+
with sentry_sdk.start_span(op="http.client", name="Fetch User Data"):
213+
response = requests.get("https://api.example.com/users")
214+
215+
# Database operation
216+
with sentry_sdk.start_span(op="db", name="Save User"):
217+
db.execute(
218+
"INSERT INTO users (name, email) VALUES (%s, %s)",
219+
(user.name, user.email),
220+
)
221+
222+
# File I/O operation
223+
with sentry_sdk.start_span(op="file.read", name="Read Config"):
224+
with open("config.json", "r") as f:
225+
config = json.load(f)
226+
```
227+
228+
### Updating the Span Status
229+
230+
You can update the status of a span to indicate whether it succeeded or failed:
231+
232+
```python
233+
import sentry_sdk
234+
235+
with sentry_sdk.start_span(op="task", name="Process Payment") as span:
236+
try:
237+
result = process_payment(payment_id)
238+
if result.success:
239+
# Mark the span as successful
240+
span.set_status("ok")
241+
else:
242+
# Mark the span as failed
243+
span.set_status("error")
244+
span.set_data("error_reason", result.error)
245+
except Exception:
246+
# Span will automatically be marked as failed when an exception occurs
247+
raise
248+
```

0 commit comments

Comments
 (0)