Skip to content

Commit 4f124dc

Browse files
feat(python): SDK 3.x version of the Span Lifecycle page (#14447)
Adding a 3.x copy of the Span Lifecycle page and changing a few things on it. Here's the actual diff of what's on the 3.x page vs the original `index.mdx`: [diff.txt](https://github.com/user-attachments/files/21404680/diff.txt) --------- Co-authored-by: Alex Krawiec <[email protected]>
1 parent f7b2fd1 commit 4f124dc

File tree

2 files changed

+248
-1
lines changed

2 files changed

+248
-1
lines changed

docs/platforms/python/tracing/span-lifecycle/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ with sentry_sdk.start_span(op="http.client", name="Fetch User Data"):
202202

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

0 commit comments

Comments
 (0)