Skip to content

Commit 12ae445

Browse files
authored
Update Tracing > Configure Sampling for SDK 3.0 and improve examples (#14345)
We're working on version 3.0 of the Python SDK in which we're changing how sampling works. Updating the `Tracing > Configure Sampling` page to reflect that, while leaving the current one as is. Also fixed a lot of incorrect examples on the existing page.
1 parent 0f4cb5b commit 12ae445

File tree

3 files changed

+400
-79
lines changed

3 files changed

+400
-79
lines changed

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

Lines changed: 62 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,20 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
5353
# Use the parent sampling decision if we have an incoming trace.
5454
# Note: we strongly recommend respecting the parent sampling decision,
5555
# as this ensures your traces will be complete!
56-
parent_sampling_decision = sampling_context.get("parent_sampled")
56+
parent_sampling_decision = sampling_context["parent_sampled"]
5757
if parent_sampling_decision is not None:
5858
return float(parent_sampling_decision)
5959

60-
ctx = sampling_context.get("transaction_context", {})
61-
name = ctx.get("name")
60+
transaction_ctx = sampling_context["transaction_context"]
61+
name = transaction_ctx["name"]
62+
op = transaction_ctx["op"]
6263

6364
# Sample all checkout transactions
64-
if name and ('/checkout' in name or
65-
ctx.get("op") == 'checkout'):
65+
if name and ('/checkout' in name or op == 'checkout'):
6666
return 1.0
6767

6868
# Sample 50% of login transactions
69-
if name and ('/login' in name or
70-
ctx.get("op") == 'login'):
69+
if name and ('/login' in name or op == 'login'):
7170
return 0.5
7271

7372
# Sample 10% of everything else
@@ -89,11 +88,11 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
8988
# Use the parent sampling decision if we have an incoming trace.
9089
# Note: we strongly recommend respecting the parent sampling decision,
9190
# as this ensures your traces will be complete!
92-
parent_sampling_decision = sampling_context.get("parent_sampled")
91+
parent_sampling_decision = sampling_context["parent_sampled"]
9392
if parent_sampling_decision is not None:
9493
return float(parent_sampling_decision)
9594

96-
ctx = sampling_context.get("transaction_context", {})
95+
custom_sampling_ctx = sampling_context["custom_sampling_context"]
9796
environment = os.environ.get("ENVIRONMENT", "development")
9897

9998
# Sample all transactions in development
@@ -102,7 +101,7 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
102101

103102
# Sample more transactions if there are recent errors
104103
# Note: hasRecentErrors is a custom attribute that needs to be set
105-
if ctx.get("data", {}).get("hasRecentErrors"):
104+
if custom_sampling_ctx.get("hasRecentErrors") is True:
106105
return 0.8
107106

108107
# Sample based on environment
@@ -120,17 +119,15 @@ sentry_sdk.init(
120119
traces_sampler=traces_sampler,
121120
)
122121

123-
# You can use the sampling function by setting custom attributes:
124-
# Option 1: When creating the transaction
125-
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
126-
# Set custom attribute
127-
transaction.set_data("hasRecentErrors", True)
128-
# Your code here
129-
130-
# Option 2: During the transaction's lifecycle
131-
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
132-
# Your code here
133-
transaction.set_data("hasRecentErrors", True) # Set custom attribute
122+
# Custom attributes need to be set on transaction start via
123+
# the `custom_sampling_context` argument in order to be available
124+
# in the traces_sampler
125+
with sentry_sdk.start_transaction(
126+
name="GET /api/users",
127+
op="http.request",
128+
custom_sampling_context={"hasRecentErrors": True},
129+
) as transaction:
130+
# your code here
134131
```
135132

136133
3. Controlling Sampling Based on User and Transaction Properties
@@ -143,33 +140,27 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
143140
# Use the parent sampling decision if we have an incoming trace.
144141
# Note: we strongly recommend respecting the parent sampling decision,
145142
# as this ensures your traces will be complete!
146-
parent_sampling_decision = sampling_context.get("parent_sampled")
143+
parent_sampling_decision = sampling_context["parent_sampled"]
147144
if parent_sampling_decision is not None:
148145
return float(parent_sampling_decision)
149146

150-
ctx = sampling_context.get("transaction_context", {})
151-
data = ctx.get("data", {})
147+
custom_sampling_ctx = sampling_context["custom_sampling_context"]
152148

153149
# Always sample for premium users
154150
# Note: user.tier is a custom attribute that needs to be set
155-
if data.get("user", {}).get("tier") == "premium":
151+
if custom_sampling_ctx.get("user", {}).get("tier") == "premium":
156152
return 1.0
157153

158154
# Sample more transactions for users experiencing errors
159155
# Note: hasRecentErrors is a custom attribute
160-
if data.get("hasRecentErrors"):
156+
if custom_sampling_ctx.get("hasRecentErrors") is True:
161157
return 0.8
162158

163159
# Sample less for high-volume, low-value paths
164-
# Note: name is an SDK-provided attribute
165-
if (ctx.get("name") or "").startswith("/api/metrics"):
160+
name = sampling_context["transaction_context"]["name"]
161+
if name and name.startswith("/api/metrics"):
166162
return 0.01
167163

168-
# Sample more for slow transactions
169-
# Note: duration_ms is a custom attribute
170-
if data.get("duration_ms", 0) > 1000: # Transactions over 1 second
171-
return 0.5
172-
173164
# Default sampling rate
174165
return 0.2
175166

@@ -180,13 +171,12 @@ sentry_sdk.init(
180171
)
181172

182173
# To set custom attributes for this example:
183-
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
184-
# Set custom attributes
185-
transaction.set_data("user", {"tier": "premium"}) # Custom user data
186-
transaction.set_data("hasRecentErrors", True) # Custom error flag
187-
transaction.set_data("duration_ms", 1500) # Custom timing data
174+
with sentry_sdk.start_transaction(
175+
name="GET /api/users",
176+
op="http.request",
177+
custom_sampling_context={"user": {"tier": "premium"}, "hasRecentErrors": True},
178+
) as transaction:
188179
# Your code here
189-
190180
```
191181

192182
4. Complex Business Logic Sampling
@@ -199,36 +189,36 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
199189
# Use the parent sampling decision if we have an incoming trace.
200190
# Note: we strongly recommend respecting the parent sampling decision,
201191
# as this ensures your traces will be complete!
202-
parent_sampling_decision = sampling_context.get("parent_sampled")
192+
parent_sampling_decision = sampling_context["parent_sampled"]
203193
if parent_sampling_decision is not None:
204194
return float(parent_sampling_decision)
205195

206-
ctx = sampling_context.get("transaction_context", {})
207-
data = ctx.get("data", {})
208-
209196
# Always sample critical business operations
210197
# Note: op is an SDK-provided attribute
211-
if ctx.get("op") in ["payment.process", "order.create", "user.verify"]:
198+
transaction_ctx = sampling_context["transaction_context"]
199+
if transaction_ctx["op"] in ["payment.process", "order.create", "user.verify"]:
212200
return 1.0
213201

202+
custom_sampling_context = sampling_context["custom_sampling_context"]
203+
214204
# Sample based on user segment
215205
# Note: user.segment is a custom attribute
216-
user_segment = data.get("user", {}).get("segment")
206+
user_segment = custom_sampling_context.get("user", {}).get("segment")
217207
if user_segment == "enterprise":
218208
return 0.8
219209
elif user_segment == "premium":
220210
return 0.5
221211

222212
# Sample based on transaction value
223213
# Note: transaction.value is a custom attribute
224-
transaction_value = data.get("transaction", {}).get("value", 0)
225-
if transaction_value > 1000: # High-value transactions
214+
transaction_value = custom_sampling_context.get("transaction", {}).get("value")
215+
if transaction_value is not None and transaction_value > 1000: # High-value transactions
226216
return 0.7
227217

228218
# Sample based on error rate in the service
229219
# Note: service.error_rate is a custom attribute
230-
error_rate = data.get("service", {}).get("error_rate", 0)
231-
if error_rate > 0.05: # Error rate above 5%
220+
error_rate = custom_sampling_context.get("service", {}).get("error_rate")
221+
if error_rate is not None and error_rate > 0.05: # Error rate above 5%
232222
return 0.9
233223

234224
# Default sampling rate
@@ -241,11 +231,11 @@ sentry_sdk.init(
241231
)
242232

243233
# To set custom attributes for this example:
244-
with sentry_sdk.start_transaction(name="Process Payment", op="payment.process") as transaction:
245-
# Set custom attributes
246-
transaction.set_data("user", {"segment": "enterprise"}) # Custom user data
247-
transaction.set_data("transaction", {"value": 1500}) # Custom transaction data
248-
transaction.set_data("service", {"error_rate": 0.03}) # Custom service data
234+
with sentry_sdk.start_transaction(
235+
name="Process Payment",
236+
op="payment.process",
237+
custom_sampling_context={"user": {"segment": "enterprise"}, "transaction": {"value": 1500}, "service": {"error_rate": 0.03}},
238+
) as transaction:
249239
# Your code here
250240

251241
```
@@ -260,31 +250,28 @@ def traces_sampler(sampling_context: SamplingContext) -> float:
260250
# Use the parent sampling decision if we have an incoming trace.
261251
# Note: we strongly recommend respecting the parent sampling decision,
262252
# as this ensures your traces will be complete!
263-
parent_sampling_decision = sampling_context.get("parent_sampled")
253+
parent_sampling_decision = sampling_context["parent_sampled"]
264254
if parent_sampling_decision is not None:
265255
return float(parent_sampling_decision)
266256

267-
ctx = sampling_context.get("transaction_context", {})
268-
data = ctx.get("data", {})
269-
270-
# Sample all slow transactions
271-
# Note: duration_ms is a custom attribute
272-
if data.get("duration_ms", 0) > 2000: # Over 2 seconds
273-
return 1.0
257+
custom_sampling_ctx = sampling_context["custom_sampling_context"]
274258

275259
# Sample more transactions with high memory usage
276260
# Note: memory_usage_mb is a custom attribute
277-
if data.get("memory_usage_mb", 0) > 500: # Over 500MB
261+
memory_usage = custom_sampling_ctx.get("memory_usage_mb")
262+
if memory_usage is not None and memory_usage > 500:
278263
return 0.8
279264

280265
# Sample more transactions with high CPU usage
281266
# Note: cpu_percent is a custom attribute
282-
if data.get("cpu_percent", 0) > 80: # Over 80% CPU
267+
cpu_percent = custom_sampling_ctx.get("cpu_percent")
268+
if cpu_percent is not None and cpu_percent > 80:
283269
return 0.8
284270

285271
# Sample more transactions with high database load
286272
# Note: db_connections is a custom attribute
287-
if data.get("db_connections", 0) > 100: # Over 100 connections
273+
db_connections = custom_sampling_ctx.get("db_connections")
274+
if db_connections is not None and db_connections > 100:
288275
return 0.7
289276

290277
# Default sampling rate
@@ -297,14 +284,12 @@ sentry_sdk.init(
297284
)
298285

299286
# To set custom attributes for this example:
300-
with sentry_sdk.start_transaction(name="Process Data", op="data.process") as transaction:
301-
# Set custom attributes
302-
transaction.set_data("duration_ms", 2500) # Custom timing data
303-
transaction.set_data("memory_usage_mb", 600) # Custom memory data
304-
transaction.set_data("cpu_percent", 85) # Custom CPU data
305-
transaction.set_data("db_connections", 120) # Custom database data
287+
with sentry_sdk.start_transaction(
288+
name="Process Data",
289+
op="data.process",
290+
custom_sampling_context={"memory_usage_mb": 600, "cpu_percent": 85, "db_connections": 120},
291+
) as transaction:
306292
# Your code here
307-
308293
```
309294
</details>
310295

@@ -315,13 +300,13 @@ When the `traces_sampler` function is called, the Sentry SDK passes a `sampling_
315300
```python
316301
{
317302
"transaction_context": {
318-
"name": str, # transaction title at creation time (SDK-provided)
319-
"op": str, # short description of transaction type (SDK-provided)
320-
"data": Optional[Dict[str, Any]] # custom data you've added to the transaction
303+
"name": str, # transaction title at creation time (SDK-provided)
304+
"op": str, # short description of transaction type (SDK-provided)
305+
"data": Optional[dict[str, Any]]
321306
},
322307
"parent_sampled": Optional[bool], # whether the parent transaction was sampled (SDK-provided)
323-
"parent_sample_rate": Optional[float], # the sample rate used by the parent (SDK-provided)
324-
"custom_sampling_context": Optional[Dict[str, Any]] # additional custom data for sampling
308+
"parent_sample_rate": Optional[float], # the sample rate used by the parent (SDK-provided)
309+
"custom_sampling_context": Optional[dict[str, Any]] # additional custom data for sampling
325310
}
326311
```
327312

@@ -336,7 +321,6 @@ The sampling context contains both SDK-provided attributes and custom attributes
336321
- `parent_sample_rate`: The sample rate used by the parent
337322

338323
**Custom Attributes:**
339-
- Any data you add to the `set_data` method on the transaction object. Use this for data that you want to include in the transaction data that gets sent to Sentry.
340324
- Any data you add to the `custom_sampling_context` parameter in `start_transaction`. Use this for data that you want to use for sampling decisions but don't want to include in the transaction data that gets sent to Sentry. Read more about sampling context [here](/platforms/python/configuration/sampling/#sampling-context).
341325

342326
## Sampling Decision Precedence

0 commit comments

Comments
 (0)