Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
426c39e
Add Python tracing span metrics documentation
Mar 21, 2025
302e288
add new documentation to and reorganize Python tracing docs
Mar 25, 2025
2f2680d
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Mar 26, 2025
dd4ad91
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Mar 26, 2025
0397fcf
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Mar 26, 2025
3c601a2
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Mar 26, 2025
2cdca6b
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Mar 26, 2025
6d4e3bf
Update docs/platforms/python/tracing/span-metrics/index.mdx
sfanahata Mar 26, 2025
d6e2490
Update docs/platforms/python/tracing/span-metrics/performance-metrics…
sfanahata Mar 26, 2025
4a3d732
updates based on PR feedback
Mar 26, 2025
084a25e
comment out python troubleshootredirect
Mar 26, 2025
a192aec
resolving yarn.lock issues
Mar 27, 2025
9770d5a
update yarn.lock - adding a blank line at the end
Mar 27, 2025
f69c5ea
fixing redirect 404 for Python profiling page
Mar 27, 2025
d808dd9
break out span lifecycle doc and resolve feedback from PR
Apr 1, 2025
c430801
resolving broken link in instrumentation index file
Apr 1, 2025
2f791b2
Update docs/platforms/python/tracing/instrumentation/index.mdx
szokeasaurusrex Apr 1, 2025
03f5d6a
order span lifecycle doc
Apr 1, 2025
137dbb8
order span lifecycle doc
Apr 1, 2025
5117a30
Update index.mdx
sfanahata Apr 1, 2025
44289dd
Update index.mdx
sfanahata Apr 1, 2025
c8dba3b
Update docs/platforms/python/tracing/span-lifecycle/index.mdx
sfanahata Apr 1, 2025
92a9a3d
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Apr 1, 2025
407c560
Update docs/platforms/python/tracing/instrumentation/index.mdx
sfanahata Apr 1, 2025
0a89965
Update index.mdx
sfanahata Apr 1, 2025
b131bbf
Update python.mdx
sfanahata Apr 1, 2025
fef0ae1
Update index.mdx
sfanahata Apr 1, 2025
61dd75e
Update index.mdx
sfanahata Apr 1, 2025
1a9da90
Update docs/platforms/python/tracing/span-metrics/index.mdx
sfanahata Apr 1, 2025
dd5d44a
Update docs/platforms/python/tracing/span-metrics/index.mdx
sfanahata Apr 1, 2025
8b00344
Update index.mdx
sfanahata Apr 1, 2025
ab9ebf7
fix span.attributes mix-up, more feedback from reviewers
Apr 3, 2025
6de450f
update span lifecycle
Apr 3, 2025
e9bb6af
updated examples based on feedback. Reverted yarn.lock change
Apr 4, 2025
e39d413
Reset yarn.lock to match master
Apr 4, 2025
d8e046c
Merge remote-tracking branch 'origin/master' into ShannonA-python-tra…
Apr 4, 2025
7e5c6e4
yarn.lock revert
Apr 4, 2025
d09e2b4
Force commit for yarn.lock
Apr 4, 2025
1519f54
update yarn.lock
Apr 4, 2025
8f17aa2
update yarn.lock
Apr 4, 2025
a2e0e4d
reset yarn.lock to match master
Apr 4, 2025
75033d4
Update yarn.lock from master
Apr 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions docs/platforms/python/profiling/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ For Profiling to work, you have to first enable [Sentry’s tracing](/concepts/k

</Alert>

### Upgrading from Older Python SDK Versions

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>.

## Enable Continuous Profiling

<Include name="feature-stage-beta.mdx" />
Expand Down
348 changes: 348 additions & 0 deletions docs/platforms/python/tracing/configure-sampling/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
---
title: Configure Sampling
description: "Learn how to configure sampling in your app."
sidebar_order: 40
---

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.

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.

## Sampling Configuration Options

### 1. Uniform Sample Rate (`traces_sample_rate`)

`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:

<PlatformContent includePath="/performance/traces-sample-rate" />

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.

### 2. Sampling Function (`traces_sampler`)

For more granular control, you can provide a `traces_sampler` function. This approach allows you to:

- Apply different sampling rates to different types of transactions
- Filter out specific transactions entirely
- Make sampling decisions based on transaction data
- Control the inheritance of sampling decisions in distributed traces
- Use custom attributes to modify sampling

<Alert>

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

</Alert>

In distributed systems, implementing inheritance logic when trace information is propagated between services will ensure consistent sampling decisions across your entire distributed trace.

<PlatformContent includePath="/performance/traces-sampler-as-sampler" />

<details>
<summary className="text-xl font-semibold">Trace Sampler Examples</summary>

#### Trace Sampler Examples

1. Prioritizing Critical User Flows

```python
def traces_sampler(sampling_context):
# Use the parent sampling decision if we have an incoming trace.
# Note: we strongly recommend respecting the parent sampling decision,
# as this ensures your traces will be complete!
parent_sampling_decision = sampling_context.get("parent_sampled")
if parent_sampling_decision is not None:
return float(parent_sampling_decision)

ctx = sampling_context.get("transaction_context", {})
name = ctx.get("name")

# Sample all checkout transactions
if name and ('/checkout' in name or
ctx.get("op") == 'checkout'):
return 1.0

# Sample 50% of login transactions
if name and ('/login' in name or
ctx.get("op") == 'login'):
return 0.5

# Sample 10% of everything else
return 0.1

sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)
```

2. Handling Different Environments and Error Rates

```python
def traces_sampler(sampling_context):
# Use the parent sampling decision if we have an incoming trace.
# Note: we strongly recommend respecting the parent sampling decision,
# as this ensures your traces will be complete!
parent_sampling_decision = sampling_context.get("parent_sampled")
if parent_sampling_decision is not None:
return float(parent_sampling_decision)

ctx = sampling_context.get("transaction_context", {})
environment = os.environ.get("ENVIRONMENT", "development")

# Sample all transactions in development
if environment == "development":
return 1.0

# Sample more transactions if there are recent errors
# Note: hasRecentErrors is a custom attribute that needs to be set
if ctx.get("data", {}).get("hasRecentErrors"):
return 0.8

# Sample based on environment
if environment == "production":
return 0.05 # 5% in production
elif environment == "staging":
return 0.2 # 20% in staging

# Default sampling rate
return 0.1

# Initialize the SDK with the sampling function
sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)

# You can use the sampling function by setting custom attributes:
# Option 1: When creating the transaction
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
# Set custom attribute
transaction.set_data("hasRecentErrors", True)
# Your code here

# Option 2: During the transaction's lifecycle
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
# Your code here
transaction.set_data("hasRecentErrors", True) # Set custom attribute
```

3. Controlling Sampling Based on User and Transaction Properties

```python
def traces_sampler(sampling_context):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example it's also not 100% clear what's coming from the SDK and what the user needs to have set for it to appear in the sampling context (user.tier, hasRecentErrors) -- might lead to misunderstandings if folks expect this data to be there from the get go. Maybe we can clarify that some of these are custom attributes that need to be set by the user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I'll add more details in the sample comments that makes it clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also added a bullet at the top of the section to call out using custom attributes as a part of useing traces_sampler.

Copy link
Contributor

@sentrivana sentrivana Apr 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, have the changes already been applied? As is the examples are still mixing prepopulated keys with custom ones and it's still not clear which is which. If I was a new user I'd be confused as to why there's a parent_sampled in my sampling_context but no data.hasRecentErrors.

I feel like the current page https://docs.sentry.io/platforms/python/configuration/sampling/#sampling-context-data does a great job of making the distinction as well as making it clear how you can get custom data into the sampling_context. I'm not sure if this section stays as is after the reorganization -- if not, can we somehow port it to this page? And if it does, can we reference it at the start of this page when mentioning custom attributes?

Copy link
Contributor Author

@sfanahata sfanahata Apr 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that example. I see what you mean now! The custom_sampling_context would be a better way to group the custom data together in the examples. I'll update them, and link to the sampling page.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the examples to more explicitly use transaction.set_data and added a note about set_data vs custom_sampling_context, with a link to the sampling page.

# Use the parent sampling decision if we have an incoming trace.
# Note: we strongly recommend respecting the parent sampling decision,
# as this ensures your traces will be complete!
parent_sampling_decision = sampling_context.get("parent_sampled")
if parent_sampling_decision is not None:
return float(parent_sampling_decision)

ctx = sampling_context.get("transaction_context", {})
data = ctx.get("data", {})

# Always sample for premium users
# Note: user.tier is a custom attribute that needs to be set
if data.get("user", {}).get("tier") == "premium":
return 1.0

# Sample more transactions for users experiencing errors
# Note: hasRecentErrors is a custom attribute
if data.get("hasRecentErrors"):
return 0.8

# Sample less for high-volume, low-value paths
# Note: name is an SDK-provided attribute
if (ctx.get("name") or "").startswith("/api/metrics"):
return 0.01

# Sample more for slow transactions
# Note: duration_ms is a custom attribute
if data.get("duration_ms", 0) > 1000: # Transactions over 1 second
return 0.5

# Default sampling rate
return 0.2

# Initialize the SDK with the sampling function
sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)

# To set custom attributes for this example:
with sentry_sdk.start_transaction(name="GET /api/users", op="http.request") as transaction:
# Set custom attributes
transaction.set_data("user", {"tier": "premium"}) # Custom user data
transaction.set_data("hasRecentErrors", True) # Custom error flag
transaction.set_data("duration_ms", 1500) # Custom timing data
# Your code here

```

4. Complex Business Logic Sampling

```python
def traces_sampler(sampling_context):
# Use the parent sampling decision if we have an incoming trace.
# Note: we strongly recommend respecting the parent sampling decision,
# as this ensures your traces will be complete!
parent_sampling_decision = sampling_context.get("parent_sampled")
if parent_sampling_decision is not None:
return float(parent_sampling_decision)

ctx = sampling_context.get("transaction_context", {})
data = ctx.get("data", {})

# Always sample critical business operations
# Note: op is an SDK-provided attribute
if ctx.get("op") in ["payment.process", "order.create", "user.verify"]:
return 1.0

# Sample based on user segment
# Note: user.segment is a custom attribute
user_segment = data.get("user", {}).get("segment")
if user_segment == "enterprise":
return 0.8
elif user_segment == "premium":
return 0.5

# Sample based on transaction value
# Note: transaction.value is a custom attribute
transaction_value = data.get("transaction", {}).get("value", 0)
if transaction_value > 1000: # High-value transactions
return 0.7

# Sample based on error rate in the service
# Note: service.error_rate is a custom attribute
error_rate = data.get("service", {}).get("error_rate", 0)
if error_rate > 0.05: # Error rate above 5%
return 0.9

# Default sampling rate
return 0.1

# Initialize the SDK with the sampling function
sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)

s# To set custom attributes for this example:
with sentry_sdk.start_transaction(name="Process Payment", op="payment.process") as transaction:
# Set custom attributes
transaction.set_data("user", {"segment": "enterprise"}) # Custom user data
transaction.set_data("transaction", {"value": 1500}) # Custom transaction data
transaction.set_data("service", {"error_rate": 0.03}) # Custom service data
# Your code here

```

5. Performance-Based Sampling

```python
def traces_sampler(sampling_context):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also make clear the attributes here have to be added and are not there by default (db_connections etc.).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only picked db_connections randomly, the other attrs are also custom (duration_ms, memory_usage_mb, cpu_percent) -- so we should probably also mark them as such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a line as a part of the section's intro that this outlined how to use custom attributes, but happy to add notes in the examples too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# Use the parent sampling decision if we have an incoming trace.
# Note: we strongly recommend respecting the parent sampling decision,
# as this ensures your traces will be complete!
parent_sampling_decision = sampling_context.get("parent_sampled")
if parent_sampling_decision is not None:
return float(parent_sampling_decision)

ctx = sampling_context.get("transaction_context", {})
data = ctx.get("data", {})

# Sample all slow transactions
# Note: duration_ms is a custom attribute
if data.get("duration_ms", 0) > 2000: # Over 2 seconds
return 1.0

# Sample more transactions with high memory usage
# Note: memory_usage_mb is a custom attribute
if data.get("memory_usage_mb", 0) > 500: # Over 500MB
return 0.8

# Sample more transactions with high CPU usage
# Note: cpu_percent is a custom attribute
if data.get("cpu_percent", 0) > 80: # Over 80% CPU
return 0.8

# Sample more transactions with high database load
# Note: db_connections is a custom attribute
if data.get("db_connections", 0) > 100: # Over 100 connections
return 0.7

# Default sampling rate
return 0.1

# Initialize the SDK with the sampling function
sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)

# To set custom attributes for this example:
with sentry_sdk.start_transaction(name="Process Data", op="data.process") as transaction:
# Set custom attributes
transaction.set_data("duration_ms", 2500) # Custom timing data
transaction.set_data("memory_usage_mb", 600) # Custom memory data
transaction.set_data("cpu_percent", 85) # Custom CPU data
transaction.set_data("db_connections", 120) # Custom database data
# Your code here

```
</details>

## The Sampling Context Object

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:

```python
{
"transaction_context": {
"name": str, # transaction title at creation time (SDK-provided)
"op": str, # short description of transaction type (SDK-provided)
"data": Optional[Dict[str, Any]] # custom data you've added to the transaction
},
"parent_sampled": Optional[bool], # whether the parent transaction was sampled (SDK-provided)
"parent_sample_rate": Optional[float], # the sample rate used by the parent (SDK-provided)
"custom_sampling_context": Optional[Dict[str, Any]] # additional custom data for sampling
}
```

### SDK-Provided vs. Custom Attributes

The sampling context contains both SDK-provided attributes and custom attributes:

**SDK-Provided Attributes:**
- `transaction_context.name`: The name of the transaction
- `transaction_context.op`: The operation type
- `parent_sampled`: Whether the parent transaction was sampled
- `parent_sample_rate`: The sample rate used by the parent

**Custom Attributes:**
- 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.
- 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).

## Sampling Decision Precedence

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

1. If a sampling decision is passed to `start_transaction`, that decision is used
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
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
4. If neither of the above, `traces_sample_rate` is used
5. If none of the above are set, no transactions are sampled. This is equivalent to setting `traces_sample_rate=0.0`

## How Sampling Propagates in Distributed Traces

Sentry uses a "head-based" sampling approach:

- A sampling decision is made in the originating service (the "head")
- This decision is propagated to all downstream services

The two key headers are:
- `sentry-trace`: Contains trace ID, span ID, and sampling decision
- `baggage`: Contains additional trace metadata including sample rate

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 number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Custom Instrumentation
sidebar_order: 40
title: Custom Trace Propagation
sidebar_order: 10
---

<PlatformContent includePath="distributed-tracing/custom-instrumentation/" />
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: Trace Propagation
title: Set Up Distributed Tracing
description: "Learn how to connect events across applications/services."
sidebar_order: 3000
sidebar_order: 30
---

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.
<PlatformContent includePath="distributed-tracing/explanation" />

## What is Distributed Tracing?

Expand Down
Loading
Loading