Skip to content

Commit 9fccf68

Browse files
sfanahataShannon AnahatacoolguyzoneShannon Anahataszokeasaurusrex
authored
Python tracing docs refresh (#13125)
* Add Python tracing span metrics documentation * add new documentation to and reorganize Python tracing docs * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/span-metrics/index.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * Update docs/platforms/python/tracing/span-metrics/performance-metrics.mdx Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> * updates based on PR feedback * comment out python troubleshootredirect * resolving yarn.lock issues * update yarn.lock - adding a blank line at the end * fixing redirect 404 for Python profiling page * break out span lifecycle doc and resolve feedback from PR * resolving broken link in instrumentation index file * Update docs/platforms/python/tracing/instrumentation/index.mdx * order span lifecycle doc * order span lifecycle doc * Update index.mdx * Update index.mdx * Update docs/platforms/python/tracing/span-lifecycle/index.mdx Co-authored-by: Cody De Arkland <cody.dearkland@sentry.io> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> * Update docs/platforms/python/tracing/instrumentation/index.mdx Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> * Update index.mdx * Update python.mdx adding parent inheritance info * Update index.mdx updating to add parent inheritance to all examples. * Update index.mdx Nix distributed tracing * Update docs/platforms/python/tracing/span-metrics/index.mdx Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> * Update docs/platforms/python/tracing/span-metrics/index.mdx Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> * Update index.mdx * fix span.attributes mix-up, more feedback from reviewers * update span lifecycle * updated examples based on feedback. Reverted yarn.lock change * Reset yarn.lock to match master * yarn.lock revert * Force commit for yarn.lock * update yarn.lock * update yarn.lock * reset yarn.lock to match master * Update yarn.lock from master --------- Co-authored-by: Shannon Anahata <shannonanahata@LQCQGMGK90.local> Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io> Co-authored-by: Shannon Anahata <shannonanahata@gmail.com> Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> Co-authored-by: Cody De Arkland <cody.dearkland@sentry.io>
1 parent 2aae768 commit 9fccf68

File tree

14 files changed

+1383
-17
lines changed

14 files changed

+1383
-17
lines changed

docs/platforms/python/profiling/index.mdx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ For Profiling to work, you have to first enable [Sentry’s tracing](/concepts/k
5151

5252
</Alert>
5353

54-
### Upgrading from Older Python SDK Versions
55-
56-
Profiling was experimental in SDK versions `1.17.0` and older. Learn how to upgrade <PlatformLink to="/profiling/troubleshooting/#ipgrading-from-older-sdk-versions">here</PlatformLink>.
57-
5854
## Enable Continuous Profiling
5955

6056
<Include name="feature-stage-beta.mdx" />
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
---
2+
title: Configure Sampling
3+
description: "Learn how to configure sampling in your app."
4+
sidebar_order: 40
5+
---
6+
7+
If you find that Sentry's tracing functionality is generating too much data, for example, if you notice your spans quota is quickly being exhausted, you can choose to sample your traces.
8+
9+
Effective sampling is key to getting the most value from Sentry's performance monitoring while minimizing overhead. The Python SDK provides two ways to control the sampling rate. You can review the options and [examples](#trace-sampler-examples) below.
10+
11+
## Sampling Configuration Options
12+
13+
### 1. Uniform Sample Rate (`traces_sample_rate`)
14+
15+
`traces_sample_rate` is a floating-point value between `0.0` and `1.0`, inclusive, which controls the probability with which each transaction will be sampled:
16+
17+
<PlatformContent includePath="/performance/traces-sample-rate" />
18+
19+
With `traces_sample_rate` set to `0.25`, each transaction in your application is randomly sampled with a probability of `0.25`, so you can expect that one in every four transactions will be sent to Sentry.
20+
21+
### 2. Sampling Function (`traces_sampler`)
22+
23+
For more granular control, you can provide a `traces_sampler` function. This approach allows you to:
24+
25+
- Apply different sampling rates to different types of transactions
26+
- Filter out specific transactions entirely
27+
- Make sampling decisions based on transaction data
28+
- Control the inheritance of sampling decisions in distributed traces
29+
- Use custom attributes to modify sampling
30+
31+
<Alert>
32+
33+
It is strongly recommended when using a custom `traces_sampler` that you respect the parent sampling decision. This ensures your traces will be complete.
34+
35+
</Alert>
36+
37+
In distributed systems, implementing inheritance logic when trace information is propagated between services will ensure consistent sampling decisions across your entire distributed trace.
38+
39+
<PlatformContent includePath="/performance/traces-sampler-as-sampler" />
40+
41+
<details>
42+
<summary className="text-xl font-semibold">Trace Sampler Examples</summary>
43+
44+
#### Trace Sampler Examples
45+
46+
1. Prioritizing Critical User Flows
47+
48+
```python
49+
def traces_sampler(sampling_context):
50+
# Use the parent sampling decision if we have an incoming trace.
51+
# Note: we strongly recommend respecting the parent sampling decision,
52+
# as this ensures your traces will be complete!
53+
parent_sampling_decision = sampling_context.get("parent_sampled")
54+
if parent_sampling_decision is not None:
55+
return float(parent_sampling_decision)
56+
57+
ctx = sampling_context.get("transaction_context", {})
58+
name = ctx.get("name")
59+
60+
# Sample all checkout transactions
61+
if name and ('/checkout' in name or
62+
ctx.get("op") == 'checkout'):
63+
return 1.0
64+
65+
# Sample 50% of login transactions
66+
if name and ('/login' in name or
67+
ctx.get("op") == 'login'):
68+
return 0.5
69+
70+
# Sample 10% of everything else
71+
return 0.1
72+
73+
sentry_sdk.init(
74+
dsn="your-dsn",
75+
traces_sampler=traces_sampler,
76+
)
77+
```
78+
79+
2. Handling Different Environments and Error Rates
80+
81+
```python
82+
def traces_sampler(sampling_context):
83+
# Use the parent sampling decision if we have an incoming trace.
84+
# Note: we strongly recommend respecting the parent sampling decision,
85+
# as this ensures your traces will be complete!
86+
parent_sampling_decision = sampling_context.get("parent_sampled")
87+
if parent_sampling_decision is not None:
88+
return float(parent_sampling_decision)
89+
90+
ctx = sampling_context.get("transaction_context", {})
91+
environment = os.environ.get("ENVIRONMENT", "development")
92+
93+
# Sample all transactions in development
94+
if environment == "development":
95+
return 1.0
96+
97+
# Sample more transactions if there are recent errors
98+
# Note: hasRecentErrors is a custom attribute that needs to be set
99+
if ctx.get("data", {}).get("hasRecentErrors"):
100+
return 0.8
101+
102+
# Sample based on environment
103+
if environment == "production":
104+
return 0.05 # 5% in production
105+
elif environment == "staging":
106+
return 0.2 # 20% in staging
107+
108+
# Default sampling rate
109+
return 0.1
110+
111+
# Initialize the SDK with the sampling function
112+
sentry_sdk.init(
113+
dsn="your-dsn",
114+
traces_sampler=traces_sampler,
115+
)
116+
117+
# You can use the sampling function by setting custom attributes:
118+
# Option 1: When creating the transaction
119+
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
120+
# Set custom attribute
121+
transaction.set_data("hasRecentErrors", True)
122+
# Your code here
123+
124+
# Option 2: During the transaction's lifecycle
125+
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
126+
# Your code here
127+
transaction.set_data("hasRecentErrors", True) # Set custom attribute
128+
```
129+
130+
3. Controlling Sampling Based on User and Transaction Properties
131+
132+
```python
133+
def traces_sampler(sampling_context):
134+
# Use the parent sampling decision if we have an incoming trace.
135+
# Note: we strongly recommend respecting the parent sampling decision,
136+
# as this ensures your traces will be complete!
137+
parent_sampling_decision = sampling_context.get("parent_sampled")
138+
if parent_sampling_decision is not None:
139+
return float(parent_sampling_decision)
140+
141+
ctx = sampling_context.get("transaction_context", {})
142+
data = ctx.get("data", {})
143+
144+
# Always sample for premium users
145+
# Note: user.tier is a custom attribute that needs to be set
146+
if data.get("user", {}).get("tier") == "premium":
147+
return 1.0
148+
149+
# Sample more transactions for users experiencing errors
150+
# Note: hasRecentErrors is a custom attribute
151+
if data.get("hasRecentErrors"):
152+
return 0.8
153+
154+
# Sample less for high-volume, low-value paths
155+
# Note: name is an SDK-provided attribute
156+
if (ctx.get("name") or "").startswith("/api/metrics"):
157+
return 0.01
158+
159+
# Sample more for slow transactions
160+
# Note: duration_ms is a custom attribute
161+
if data.get("duration_ms", 0) > 1000: # Transactions over 1 second
162+
return 0.5
163+
164+
# Default sampling rate
165+
return 0.2
166+
167+
# Initialize the SDK with the sampling function
168+
sentry_sdk.init(
169+
dsn="your-dsn",
170+
traces_sampler=traces_sampler,
171+
)
172+
173+
# To set custom attributes for this example:
174+
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
175+
# Set custom attributes
176+
transaction.set_data("user", {"tier": "premium"}) # Custom user data
177+
transaction.set_data("hasRecentErrors", True) # Custom error flag
178+
transaction.set_data("duration_ms", 1500) # Custom timing data
179+
# Your code here
180+
181+
```
182+
183+
4. Complex Business Logic Sampling
184+
185+
```python
186+
def traces_sampler(sampling_context):
187+
# Use the parent sampling decision if we have an incoming trace.
188+
# Note: we strongly recommend respecting the parent sampling decision,
189+
# as this ensures your traces will be complete!
190+
parent_sampling_decision = sampling_context.get("parent_sampled")
191+
if parent_sampling_decision is not None:
192+
return float(parent_sampling_decision)
193+
194+
ctx = sampling_context.get("transaction_context", {})
195+
data = ctx.get("data", {})
196+
197+
# Always sample critical business operations
198+
# Note: op is an SDK-provided attribute
199+
if ctx.get("op") in ["payment.process", "order.create", "user.verify"]:
200+
return 1.0
201+
202+
# Sample based on user segment
203+
# Note: user.segment is a custom attribute
204+
user_segment = data.get("user", {}).get("segment")
205+
if user_segment == "enterprise":
206+
return 0.8
207+
elif user_segment == "premium":
208+
return 0.5
209+
210+
# Sample based on transaction value
211+
# Note: transaction.value is a custom attribute
212+
transaction_value = data.get("transaction", {}).get("value", 0)
213+
if transaction_value > 1000: # High-value transactions
214+
return 0.7
215+
216+
# Sample based on error rate in the service
217+
# Note: service.error_rate is a custom attribute
218+
error_rate = data.get("service", {}).get("error_rate", 0)
219+
if error_rate > 0.05: # Error rate above 5%
220+
return 0.9
221+
222+
# Default sampling rate
223+
return 0.1
224+
225+
# Initialize the SDK with the sampling function
226+
sentry_sdk.init(
227+
dsn="your-dsn",
228+
traces_sampler=traces_sampler,
229+
)
230+
231+
s# To set custom attributes for this example:
232+
with sentry_sdk.start_transaction(name="Process Payment", op="payment.process") as transaction:
233+
# Set custom attributes
234+
transaction.set_data("user", {"segment": "enterprise"}) # Custom user data
235+
transaction.set_data("transaction", {"value": 1500}) # Custom transaction data
236+
transaction.set_data("service", {"error_rate": 0.03}) # Custom service data
237+
# Your code here
238+
239+
```
240+
241+
5. Performance-Based Sampling
242+
243+
```python
244+
def traces_sampler(sampling_context):
245+
# Use the parent sampling decision if we have an incoming trace.
246+
# Note: we strongly recommend respecting the parent sampling decision,
247+
# as this ensures your traces will be complete!
248+
parent_sampling_decision = sampling_context.get("parent_sampled")
249+
if parent_sampling_decision is not None:
250+
return float(parent_sampling_decision)
251+
252+
ctx = sampling_context.get("transaction_context", {})
253+
data = ctx.get("data", {})
254+
255+
# Sample all slow transactions
256+
# Note: duration_ms is a custom attribute
257+
if data.get("duration_ms", 0) > 2000: # Over 2 seconds
258+
return 1.0
259+
260+
# Sample more transactions with high memory usage
261+
# Note: memory_usage_mb is a custom attribute
262+
if data.get("memory_usage_mb", 0) > 500: # Over 500MB
263+
return 0.8
264+
265+
# Sample more transactions with high CPU usage
266+
# Note: cpu_percent is a custom attribute
267+
if data.get("cpu_percent", 0) > 80: # Over 80% CPU
268+
return 0.8
269+
270+
# Sample more transactions with high database load
271+
# Note: db_connections is a custom attribute
272+
if data.get("db_connections", 0) > 100: # Over 100 connections
273+
return 0.7
274+
275+
# Default sampling rate
276+
return 0.1
277+
278+
# Initialize the SDK with the sampling function
279+
sentry_sdk.init(
280+
dsn="your-dsn",
281+
traces_sampler=traces_sampler,
282+
)
283+
284+
# To set custom attributes for this example:
285+
with sentry_sdk.start_transaction(name="Process Data", op="data.process") as transaction:
286+
# Set custom attributes
287+
transaction.set_data("duration_ms", 2500) # Custom timing data
288+
transaction.set_data("memory_usage_mb", 600) # Custom memory data
289+
transaction.set_data("cpu_percent", 85) # Custom CPU data
290+
transaction.set_data("db_connections", 120) # Custom database data
291+
# Your code here
292+
293+
```
294+
</details>
295+
296+
## The Sampling Context Object
297+
298+
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:
299+
300+
```python
301+
{
302+
"transaction_context": {
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]] # custom data you've added to the transaction
306+
},
307+
"parent_sampled": Optional[bool], # whether the parent transaction was sampled (SDK-provided)
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
310+
}
311+
```
312+
313+
### SDK-Provided vs. Custom Attributes
314+
315+
The sampling context contains both SDK-provided attributes and custom attributes:
316+
317+
**SDK-Provided Attributes:**
318+
- `transaction_context.name`: The name of the transaction
319+
- `transaction_context.op`: The operation type
320+
- `parent_sampled`: Whether the parent transaction was sampled
321+
- `parent_sample_rate`: The sample rate used by the parent
322+
323+
**Custom Attributes:**
324+
- 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.
325+
- 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).
326+
327+
## Sampling Decision Precedence
328+
329+
When multiple sampling mechanisms could apply, Sentry follows this order of precedence:
330+
331+
1. If a sampling decision is passed to `start_transaction`, that decision is used
332+
2. If `traces_sampler` is defined, its decision is used. Although the `traces_sampler` can override the parent sampling decision, most users will want to ensure their `traces_sampler` respects the parent sampling decision
333+
3. If no `traces_sampler` is defined, but there is a parent sampling decision from an incoming distributed trace, we use the parent sampling decision
334+
4. If neither of the above, `traces_sample_rate` is used
335+
5. If none of the above are set, no transactions are sampled. This is equivalent to setting `traces_sample_rate=0.0`
336+
337+
## How Sampling Propagates in Distributed Traces
338+
339+
Sentry uses a "head-based" sampling approach:
340+
341+
- A sampling decision is made in the originating service (the "head")
342+
- This decision is propagated to all downstream services
343+
344+
The two key headers are:
345+
- `sentry-trace`: Contains trace ID, span ID, and sampling decision
346+
- `baggage`: Contains additional trace metadata including sample rate
347+
348+
The Sentry Python SDK automatically attaches these headers to outgoing HTTP requests when using auto-instrumentation with libraries like `requests`, `urllib3`, or `httpx`. For other communication channels, you can manually propagate trace information. Learn more about customizing tracing in [custom trace propagation](/platforms/python/tracing/distributed-tracing/custom-trace-propagation/)
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Custom Instrumentation
3-
sidebar_order: 40
2+
title: Custom Trace Propagation
3+
sidebar_order: 10
44
---
55

66
<PlatformContent includePath="distributed-tracing/custom-instrumentation/" />

docs/platforms/python/tracing/trace-propagation/index.mdx renamed to docs/platforms/python/tracing/distributed-tracing/index.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
title: Trace Propagation
2+
title: Set Up Distributed Tracing
33
description: "Learn how to connect events across applications/services."
4-
sidebar_order: 3000
4+
sidebar_order: 30
55
---
66

7-
If the overall application landscape that you want to observe with Sentry consists of more than just a single service or application, distributed tracing can add a lot of value.
7+
<PlatformContent includePath="distributed-tracing/explanation" />
88

99
## What is Distributed Tracing?
1010

0 commit comments

Comments
 (0)