Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,12 @@ def _get_options(*args: "Optional[str]", **kwargs: "Any") -> "Dict[str, Any]":
stacklevel=2,
)

if rv["ignore_spans"] and not has_span_streaming_enabled(rv):
warnings.warn(
"The `ignore_spans` parameter only works when `trace_lifecycle` is set to `stream`.",
stacklevel=2,
)
Comment thread
ericapisani marked this conversation as resolved.

return rv


Expand Down
8 changes: 8 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,7 @@ def __init__(
server_name: "Optional[str]" = None,
shutdown_timeout: float = 2,
integrations: "Sequence[sentry_sdk.integrations.Integration]" = [], # noqa: B006
ignore_spans: "Optional[IgnoreSpansConfig]" = None,
in_app_include: "List[str]" = [], # noqa: B006
in_app_exclude: "List[str]" = [], # noqa: B006
default_integrations: bool = True,
Expand All @@ -1293,6 +1294,7 @@ def __init__(
ca_certs: "Optional[str]" = None,
propagate_traces: bool = True,
traces_sample_rate: "Optional[float]" = None,
trace_lifecycle: "Optional[Literal['static', 'stream']]" = None,
traces_sampler: "Optional[TracesSampler]" = None,
profiles_sample_rate: "Optional[float]" = None,
profiles_sampler: "Optional[TracesSampler]" = None,
Expand Down Expand Up @@ -1757,6 +1759,12 @@ def __init__(
:param stream_gen_ai_spans: When set, generative AI spans are sent in a new transport format to
reduce downstream data loss.

:param trace_lifecycle: Controls how traces are sent. Set to `"stream"` to send spans as they
finish, or `"static"` to send a completed trace as a transaction event.

:param ignore_spans: A sequence of span-matching rules. Matching spans are ignored when
`trace_lifecycle="stream"` is enabled.

:param _experiments: Dictionary of experimental, opt-in features that are not yet stable.

``data_collection`` (EXPERIMENTAL): structured configuration controlling what data integrations
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
The API in this file is only meant to be used in span streaming mode.

You can enable span streaming mode via
sentry_sdk.init(_experiments={"trace_lifecycle": "stream"}).
sentry_sdk.init(trace_lifecycle="stream").
"""

import sys
Expand Down Expand Up @@ -854,7 +854,7 @@ def get_current_span(
Returns the currently active span on the scope if the span is a `StreamedSpan`, otherwise `None`.

This function will only return a non-`None` value when the streaming trace lifecycle is enabled.
To enable the lifecycle, pass `_experiments={"trace_lifecycle": "stream"}` to `sentry.init()`.
To enable the lifecycle, pass `trace_lifecycle="stream"` to `sentry.init()`.
"""
scope = scope or sentry_sdk.get_current_scope()
current_span = scope.streamed_span
Expand Down
20 changes: 18 additions & 2 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,14 @@ def has_span_streaming_enabled(options: "Optional[dict[str, Any]]") -> bool:
if options is None:
return False

return (options.get("_experiments") or {}).get("trace_lifecycle") == "stream"
is_enabled_in_experiment_config = (options.get("_experiments") or {}).get(
"trace_lifecycle"
) == "stream"

if options.get("trace_lifecycle") is not None:
return options.get("trace_lifecycle") == "stream"

return is_enabled_in_experiment_config


def should_truncate_gen_ai_input(options: "Optional[dict[str, Any]]") -> bool:
Expand Down Expand Up @@ -1647,7 +1654,16 @@ def _make_sampling_decision(
def is_ignored_span(name: str, attributes: "Optional[Attributes]") -> bool:
"""Determine if a span fits one of the rules in ignore_spans."""
client = sentry_sdk.get_client()
ignore_spans = (client.options.get("_experiments") or {}).get("ignore_spans")
is_ignored_at_top_level = client.options.get("ignore_spans", None)
is_ignored_in_experiment_config = (client.options.get("_experiments") or {}).get(
"ignore_spans"
)

ignore_spans = (
is_ignored_at_top_level
if is_ignored_at_top_level is not None
else is_ignored_in_experiment_config
)

if not ignore_spans:
return False
Expand Down
23 changes: 23 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
import sys
import time
import warnings
from collections import Counter, defaultdict
from collections.abc import Mapping
from textwrap import dedent
Expand Down Expand Up @@ -1524,6 +1525,28 @@ def test_enable_tracing_deprecated(sentry_init, enable_tracing):
sentry_init(enable_tracing=enable_tracing)


def test_ignore_spans_warns_without_streaming(sentry_init):
with pytest.warns(UserWarning, match=r"`ignore_spans` parameter only works"):
sentry_init(ignore_spans=["/health"], trace_lifecycle="static")


@pytest.mark.parametrize(
"options",
[
{"ignore_spans": ["/health"], "trace_lifecycle": "stream"},
{"ignore_spans": ["/health"], "_experiments": {"trace_lifecycle": "stream"}},
{},
],
)
def test_ignore_spans_does_not_warn(sentry_init, options):
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
sentry_init(**options)

ignore_spans_warnings = [w for w in caught if "ignore_spans" in str(w.message)]
assert ignore_spans_warnings == []


def make_options_transport_cls():
"""Make an options transport class that captures the options passed to it."""
# We need a unique class for each test so that the options are not
Expand Down
Loading
Loading