Configure Sampling
Learn how to configure sampling in your app using the Sentry Python SDK.
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 below.
This page covers both transaction mode (default) and stream mode. See Streamed Spans to learn more.
traces_sample_rate is a floating-point value between 0.0 and 1.0, inclusive, which controls the probability with which each transaction (or service spans in stream mode) will be sampled. This works the same way in both transaction mode and stream mode:
sentry_sdk.init(
# ...
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for tracing.
# We recommend adjusting this value in production.
traces_sample_rate=1.0,
)
sentry_sdk.init(
# ...
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for tracing.
# We recommend adjusting this value in production.
traces_sample_rate=1.0,
)
With traces_sample_rate set to 0.25, each transaction/service span in your application is randomly sampled with a probability of 0.25, so you can expect that one in every four transactions/service spans will be sent to Sentry.
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/service spans
- Filter out specific transactions/service spans entirely
- Make sampling decisions based on transaction/service span data
- Control the inheritance of sampling decisions in distributed traces
- Use custom attributes to modify sampling
It is strongly recommended when using a custom traces_sampler that you respect the parent sampling decision. This ensures your traces will be complete.
In distributed systems, implementing inheritance logic when trace information is propagated between services will ensure consistent sampling decisions across your entire distributed trace.
import sentry_sdk
from sentry_sdk.types import SamplingContext
def traces_sampler(sampling_context: SamplingContext) -> float:
# 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["parent_sampled"]
if parent_sampling_decision is not None:
return float(parent_sampling_decision)
# Examine provided sampling context along with anything in the
# global namespace to compute the sample rate for this transaction
if "...":
# These are important - take a big sample
return 0.5
elif "...":
# These are less important - only take 1%
return 0.01
elif "...":
# These aren't worth tracking - drop these transactions
return 0
# Default sample rate
return 0.1
sentry_sdk.init(
# ...
traces_sampler=traces_sampler,
)
import sentry_sdk
from sentry_sdk.types import SamplingContext
def traces_sampler(sampling_context: SamplingContext) -> float:
# 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["parent_sampled"]
if parent_sampling_decision is not None:
return float(parent_sampling_decision)
# Examine provided sampling context along with anything in the
# global namespace to compute the sample rate for this transaction
if "...":
# These are important - take a big sample
return 0.5
elif "...":
# These are less important - only take 1%
return 0.01
elif "...":
# These aren't worth tracking - drop these transactions
return 0
# Default sample rate
return 0.1
sentry_sdk.init(
# ...
traces_sampler=traces_sampler,
)
import sentry_sdk
from sentry_sdk.types import SamplingContext
def traces_sampler(sampling_context: SamplingContext) -> float:
# 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["parent_sampled"]
if parent_sampling_decision is not None:
return float(parent_sampling_decision)
# Examine provided sampling context along with anything in the
# global namespace to compute the sample rate for this span
if "...":
# These are important - take a big sample
return 0.5
elif "...":
# These are less important - only take 1%
return 0.01
elif "...":
# These aren't worth tracking - drop these spans
return 0
# Default sample rate
return 0.1
sentry_sdk.init(
# ...
traces_sampler=traces_sampler,
trace_lifecycle="stream",
)
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:
{
"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]]
},
"parent_sampled": Optional[bool], # whether the parent transaction was sampled (SDK-provided)
... # additional custom data for sampling provided via custom_sampling_context
}
{
"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]]
},
"parent_sampled": Optional[bool], # whether the parent transaction was sampled (SDK-provided)
... # additional custom data for sampling provided via custom_sampling_context
}
{
"transaction_context": {
"name": str, # span title at creation time (SDK-provided)
"op": str, # short description of span type (SDK-provided)
"data": dict[str, Any] # attributes set at span-creation time (SDK- and user-provided)
},
"parent_sampled": Optional[bool], # whether the parent span was sampled (SDK-provided)
... # additional custom key-value pairs for sampling set via custom sampling context
}
{
"transaction_context": {
"name": str, # span title at creation time (SDK-provided)
"op": str, # short description of span type (SDK-provided)
"data": dict[str, Any] # attributes set at span-creation time (SDK- and user-provided)
},
"parent_sampled": Optional[bool], # whether the parent span was sampled (SDK-provided)
... # additional custom key-value pairs for sampling set via custom sampling context
}
The sampling context contains both SDK-provided attributes and custom attributes:
SDK-Provided Attributes:
transaction_context.name: The name of the transactiontransaction_context.op: The operation typeparent_sampled: Whether the parent transaction was sampled
Custom Attributes:
- Any data you add to the
custom_sampling_contextparameter instart_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.
SDK-Provided Attributes:
transaction_context.name: The name of the spantransaction_context.data: Attributes set on the span at creation time, includingsentry.opparent_sampled: Whether the parent span was sampled
Custom Attributes:
- Any data you add via
sentry_sdk.Scope.set_custom_sampling_context(), called aftercontinue_traceand beforestart_span. Use this for data that you want to use for sampling decisions but don't want to include in the span data that gets sent to Sentry. Read more about sampling context here.
When multiple sampling mechanisms could apply, Sentry follows this order of precedence:
- If a sampling decision is passed to
start_transaction, that decision is used. - If
traces_sampleris defined, its decision is used. Although thetraces_samplercan override the parent sampling decision, most users will want to ensure theirtraces_samplerrespects the parent sampling decision. - If no
traces_sampleris defined, but there is a parent sampling decision from an incoming distributed trace, we use the parent sampling decision. - If neither of the above,
traces_sample_rateis used. - If none of the above are set, no transactions are sampled. This is equivalent to setting
traces_sample_rate=0.0.
- If
traces_sampleris defined, its decision is used. Although thetraces_samplercan override the parent sampling decision, most users will want to ensure theirtraces_samplerrespects the parent sampling decision. - If no
traces_sampleris defined, but there is a parent sampling decision from an incoming distributed trace, we use the parent sampling decision. - If neither of the above,
traces_sample_rateis used. - If none of the above are set, no spans are sampled. This is equivalent to setting
traces_sample_rate=0.0.
Sampling decisions are made when spans are created. Child spans inherit the sampling decision of their parent span unless filtered by ignore_spans.
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 decisionbaggage: 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.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").