Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 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
285 changes: 285 additions & 0 deletions docs/platforms/python/tracing/configure-sampling/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
---
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, implement inheritance logic when trace information is propagated between services. This ensures 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 by using custom attributes
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

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

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
if data.get("user", {}).get("tier") == "premium":
return 1.0

# Sample more transactions for users experiencing errors
if data.get("hasRecentErrors"):
return 0.8

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

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

# Default sampling rate
return 0.2

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

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
if ctx.get("op") in ["payment.process", "order.create", "user.verify"]:
return 1.0

# Sample based on user segment
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
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
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

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

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
if data.get("duration_ms", 0) > 2000: # Over 2 seconds
return 1.0

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

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

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

# Default sampling rate
return 0.1

sentry_sdk.init(
dsn="your-dsn",
traces_sampler=traces_sampler,
)
```
</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
"op": str, # short description of transaction type, like "http.request"
"data": Optional[Dict[str, Any]] # other transaction data
},
"parent_sampled ": Optional[bool], # whether the parent transaction was sampled, `None` if no parent or if the parent has no sampling decision
"parent_sample_rate": Optional[float], # the sample rate used by the parent (if any)
"transaction_context": Optional[Dict[str, Any]], # custom context data
"custom_sampling_context": Optional[Dict[str, Any]] # additional custom data for sampling
}
```

<b>Additional common types used in</b> `sampling_context`<b>:</b>
- str: for text values (names, operations, etc.)
- bool: for true/false values
- float: for decimal numbers (like sample rates)
- Dict[str, Any]: for dictionaries with string keys and any type of values
- Optional[Type]: for values that might be None

## 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
20 changes: 17 additions & 3 deletions docs/platforms/python/tracing/instrumentation/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
---
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure I agree with the changes to this page. This page is meant as an introduction to instrumenting traces. Since most instrumentation is done automatically by the Sentry SDK, any custom instrumentation steps, which are being described on this page, are advanced topics.

I think the changes to this page should be reverted. We can describe custom instrumentation on a "custom instrumentation" subpage.

Copy link
Contributor

Choose a reason for hiding this comment

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

I partially agree. When i made these changes for the JavaScipt docs, I wanted to name the section custom instrumentaiton instead, but there was a few pages that tied closely to instrumentation that seemed to make sense nesting underneath, so i went with "instrumentation".

I didn't feel like it really landed as an introduction to instrumenting traces; since most of the configurations were custom - or things that someone instrumenting would be in making custom adjustments to.

Ill bring this up when @mydea and I sync on the JS changes this week and see their thought. But i'd love your opinion more here too (knowing the logic i was tracing).

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 can simplify this page to be more of an intro to the section, and link down to custom instrumentation for more details. @codyde - let me know what comes from your convo this week, 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.

I turned this version of the instrumentation index file into a separate span lifecycle file. If we agree on this format, I'll make the change in JS too.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, this looks quite a bit better!

title: Instrumentation
description: "Learn how to instrument tracing in your app."
sidebar_order: 20
description: "Learn what Sentry instruments automatically, and how to configure spans to capture tracing data on any action in your app."
sidebar_order: 50
---

<PageGrid />
<Alert>

To capture transactions and spans customized to your organization's needs, you must first <PlatformLink to="/tracing/">set up tracing</PlatformLink>.

</Alert>

There are two ways that instrumentation is applied to your application:

## Automatic Instrumentation

Many integrations for popular frameworks automatically capture transactions that can be sent to Sentry. Read more about automatic instrumentation [here](/platforms/python/tracing/instrumentation/automatic-instrumentation/).

## Custom Instrumentation
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. Learn more about span lifecycles [here](/platforms/python/tracing/span-lifecycle/).

Loading
Loading