Skip to content

Commit ab9ebf7

Browse files
author
Shannon Anahata
committed
fix span.attributes mix-up, more feedback from reviewers
1 parent 8b00344 commit ab9ebf7

File tree

5 files changed

+125
-308
lines changed

5 files changed

+125
-308
lines changed

docs/platforms/python/tracing/configure-sampling/index.mdx

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ For more granular control, you can provide a `traces_sampler` function. This app
2626
- Filter out specific transactions entirely
2727
- Make sampling decisions based on transaction data
2828
- Control the inheritance of sampling decisions in distributed traces
29+
- Use custom attributes to modify sampling
2930

3031
<Alert>
3132

3233
It is strongly recommended when using a custom `traces_sampler` that you respect the parent sampling decision. This ensures your traces will be complete.
3334

3435
</Alert>
3536

37+
In distributed systems, implement inheritance logic when trace information is propagated between services. This ensures consistent sampling decisions across your entire distributed trace
38+
3639
<PlatformContent includePath="/performance/traces-sampler-as-sampler" />
3740

3841
<details>
@@ -52,7 +55,7 @@ def traces_sampler(sampling_context):
5255
return float(parent_sampling_decision)
5356

5457
ctx = sampling_context.get("transaction_context", {})
55-
name = ctx.get("name", "")
58+
name = ctx.get("name")
5659

5760
# Sample all checkout transactions
5861
if name and ('/checkout' in name or
@@ -91,8 +94,8 @@ def traces_sampler(sampling_context):
9194
if environment == "development":
9295
return 1.0
9396

94-
# Sample more transactions if there are recent errors
95-
if ctx.get("data", {}).get("hasRecentErrors"):
97+
# Sample more transactions if there are recent errors by using custom attributes
98+
if ctx.get("data", {}).get("hasRecentErrors"):
9699
return 0.8
97100

98101
# Sample based on environment
@@ -101,7 +104,8 @@ def traces_sampler(sampling_context):
101104
elif environment == "staging":
102105
return 0.2 # 20% in staging
103106

104-
return 0.1 # 10% default
107+
# Default sampling rate
108+
return 0.1
105109

106110
sentry_sdk.init(
107111
dsn="your-dsn",
@@ -132,17 +136,13 @@ def traces_sampler(sampling_context):
132136
return 0.8
133137

134138
# Sample less for high-volume, low-value paths
135-
if ctx.get("name", "").startswith("/api/metrics"):
139+
if (ctx.get("name") or "").startswith("/api/metrics"):
136140
return 0.01
137141

138142
# Sample more for slow transactions
139143
if data.get("duration_ms", 0) > 1000: # Transactions over 1 second
140144
return 0.5
141145

142-
# If there's a parent sampling decision, respect it
143-
if sampling_context.get("parent_sampled") is not None:
144-
return sampling_context["parent_sampled"]
145-
146146
# Default sampling rate
147147
return 0.2
148148

@@ -187,10 +187,6 @@ def traces_sampler(sampling_context):
187187
if error_rate > 0.05: # Error rate above 5%
188188
return 0.9
189189

190-
# Inherit parent sampling decision if available
191-
if sampling_context.get("parent_sampled") is not None:
192-
return sampling_context["parent_sampled"]
193-
194190
# Default sampling rate
195191
return 0.1
196192

@@ -226,7 +222,7 @@ def traces_sampler(sampling_context):
226222
if data.get("cpu_percent", 0) > 80: # Over 80% CPU
227223
return 0.8
228224

229-
# Sample more transactions with high database load
225+
# Sample more transactions with high database load using custom attributes
230226
if data.get("db_connections", 0) > 100: # Over 100 connections
231227
return 0.7
232228

@@ -242,7 +238,7 @@ sentry_sdk.init(
242238

243239
## The Sampling Context Object
244240

245-
When the `traces_sampler` function is called, the Sentry SDK passes a `sampling_context` object with information from the relevant span to help make sampling decisions:
241+
When the `traces_sampler` function is called, the Sentry SDK passes a `sampling_context` object with information from the relevant span to help make sampling decisions:
246242

247243
```python
248244
{
@@ -251,7 +247,7 @@ When the `traces_sampler` function is called, the Sentry SDK passes a `sampling
251247
"op": str, # short description of transaction type, like "http.request"
252248
"data": Optional[Dict[str, Any]] # other transaction data
253249
},
254-
"parent_sampled ": Optional[bool] | None, # whether the parent transaction was sampled, `None` if no parent
250+
"parent_sampled ": Optional[bool], # whether the parent transaction was sampled, `None` if no parent or if the parent has no sampling decision
255251
"parent_sample_rate": Optional[float], # the sample rate used by the parent (if any)
256252
"transaction_context": Optional[Dict[str, Any]], # custom context data
257253
"custom_sampling_context": Optional[Dict[str, Any]] # additional custom data for sampling
@@ -265,30 +261,6 @@ When the `traces_sampler` function is called, the Sentry SDK passes a `sampling
265261
- Dict[str, Any]: for dictionaries with string keys and any type of values
266262
- Optional[Type]: for values that might be None
267263

268-
## Inheritance in Distributed Tracing
269-
270-
In distributed systems, trace information is propagated between services. You can implement inheritance logic like this:
271-
272-
```python
273-
def traces_sampler(sampling_context):
274-
# Examine provided context data
275-
if "transaction_context" in sampling_context:
276-
name = sampling_context["transaction_context"].get("name", "")
277-
278-
# Apply specific rules first
279-
if "critical-path" in name:
280-
return 1.0 # Always sample
281-
282-
# Inherit parent sampling decision if available
283-
if sampling_context.get("parent_sampled") is not None:
284-
return sampling_context["parent_sampled"]
285-
286-
# Otherwise use a default rate
287-
return 0.1
288-
```
289-
290-
This approach ensures consistent sampling decisions across your entire distributed trace. All transactions in a given trace will share the same sampling decision, preventing broken or incomplete traces.
291-
292264
## Sampling Decision Precedence
293265

294266
When multiple sampling mechanisms could apply, Sentry follows this order of precedence:

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

Lines changed: 20 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ To capture transactions and spans customized to your organization's needs, you m
1010

1111
</Alert>
1212

13-
To add custom attributes or performance data to your applications traces, you need to attach these attributes to the trace's [spans](/concepts/key-terms/tracing/distributed-tracing/#traces-transactions-and-spans). Spans represent the individual actions that occur within an overall application interaction. This might include items loading, database calls, API returns, or more.
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.
1414

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

@@ -35,20 +35,7 @@ with sentry_sdk.start_span(op="task", name="Create User"):
3535
# The span automatically ends here when the 'with' block exits
3636
```
3737

38-
Alternatively, you can create spans manually and control their lifecycle yourself, which gives you more flexibility but also more responsibility:
39-
40-
```python
41-
import sentry_sdk
42-
43-
span = sentry_sdk.start_span(op="task", name="Create User")
44-
try:
45-
# Your code here
46-
finally:
47-
# Remember to always end your spans
48-
# If you don't call span.finish(), the span will remain open indefinitely,
49-
# causing memory leaks and incorrect timing data
50-
span.finish()
51-
```
38+
You can call the context manager's `__enter__` and `__exit__` methods to more explicitly control the span's lifecycle.
5239

5340
## Span Context and Nesting
5441

@@ -77,7 +64,6 @@ The following options can be used when creating spans:
7764
| ------------- | --------------- | ----------------------------------------------- |
7865
| `op` | `string` | The operation of the span. |
7966
| `name` | `string` | The name of the span. |
80-
| `attributes` | `dict` | Additional attributes to set on the span. |
8167
| `start_time` | `datetime/float`| The start time of the span. |
8268

8369
## Using the Context Manager
@@ -91,8 +77,8 @@ with sentry_sdk.start_span(op="db", name="Query Users") as span:
9177
# Perform a database query
9278
users = db.query("SELECT * FROM users")
9379

94-
# You can set attributes on the span
95-
span.set_attribute("user_count", len(users))
80+
# You can set data on the span
81+
span.set_data("user_count", len(users))
9682
```
9783

9884
The context manager also correctly handles exceptions, marking the span as failed if an exception occurs:
@@ -110,41 +96,6 @@ except Exception:
11096
pass
11197
```
11298

113-
## Creating Spans Manually
114-
115-
In some cases, you might need more control over when a span starts and ends. For this, you can create spans manually:
116-
117-
```python
118-
import sentry_sdk
119-
120-
def process_file(file_path):
121-
# Create a span
122-
span = sentry_sdk.start_span(op="file", name="Process File")
123-
124-
try:
125-
# Your processing logic
126-
with open(file_path, 'r') as f:
127-
content = f.read()
128-
129-
# Set additional attributes
130-
span.set_attribute("file_size", len(content))
131-
132-
# Process the content
133-
result = process_content(content)
134-
135-
return result
136-
except Exception as e:
137-
# Mark the span as failed
138-
span.set_status("error")
139-
# You can also add error details as attributes
140-
span.set_attribute("error_type", type(e).__name__)
141-
span.set_attribute("error_message", str(e))
142-
raise
143-
finally:
144-
# Always end the span
145-
span.finish()
146-
```
147-
14899
## Getting the Current Span
149100

150101
You can access the currently active span using `sentry_sdk.get_current_span()`:
@@ -155,7 +106,7 @@ import sentry_sdk
155106
# Get the current active span
156107
current_span = sentry_sdk.get_current_span()
157108
if current_span:
158-
current_span.set_attribute("key", "value")
109+
current_span.set_data("key", "value")
159110
```
160111

161112
## Working with Transactions
@@ -174,6 +125,10 @@ with sentry_sdk.start_transaction(name="Background Task", op="task") as transact
174125
pass
175126
```
176127

128+
## Distributed Tracing
129+
130+
See <PlatformLink to="/tracing/distributed-tracing/">Distributed Tracing</PlatformLink> for details on how to propagate trace information across different services or applications.
131+
177132
## Improving Span Data
178133

179134
### Adding Span Attributes
@@ -183,16 +138,12 @@ Span attributes customize information you can get through tracing. This informat
183138
```python
184139
import sentry_sdk
185140

186-
with sentry_sdk.start_span(op="db", name="Query Users", attributes={
187-
"db.query": "SELECT * FROM users WHERE active = true",
188-
"db.system": "postgresql",
189-
"server.address": "db.example.com"
190-
}) as span:
141+
with sentry_sdk.start_span(op="db", name="Query Users") as span:
191142
# Execute the query
192143
users = db.query("SELECT * FROM users WHERE active = true")
193144

194-
# You can add more attributes during execution
195-
span.set_attribute("result_count", len(users))
145+
# You can add more data during execution
146+
span.set_data("result_count", len(users))
196147
```
197148

198149
You can also add attributes to an existing span:
@@ -203,9 +154,9 @@ import sentry_sdk
203154
# Get the current span
204155
span = sentry_sdk.get_current_span()
205156
if span:
206-
# Set individual attributes
207-
span.set_attribute("user_id", user.id)
208-
span.set_attribute("request_size", len(request.body))
157+
# Set individual data points
158+
span.set_data("user_id", user.id)
159+
span.set_data("request_size", len(request.body))
209160
```
210161

211162
### Adding Attributes to All Spans
@@ -259,8 +210,10 @@ with sentry_sdk.start_span(op="http.client", name="Fetch User Data"):
259210

260211
# Database operation
261212
with sentry_sdk.start_span(op="db", name="Save User"):
262-
db.execute("INSERT INTO users (name, email) VALUES (%s, %s)",
263-
(user.name, user.email))
213+
db.execute(
214+
"INSERT INTO users (name, email) VALUES (%s, %s)",
215+
(user.name, user.email),
216+
)
264217

265218
# File I/O operation
266219
with sentry_sdk.start_span(op="file.read", name="Read Config"):
@@ -284,7 +237,7 @@ with sentry_sdk.start_span(op="task", name="Process Payment") as span:
284237
else:
285238
# Mark the span as failed
286239
span.set_status("error")
287-
span.set_attribute("error_reason", result.error)
240+
span.set_data("error_reason", result.error)
288241
except Exception:
289242
# Span will automatically be marked as failed when an exception occurs
290243
raise

0 commit comments

Comments
 (0)