diff --git a/sentry_sdk/traces.py b/sentry_sdk/traces.py index 1f96f4a383..dd26a73f34 100644 --- a/sentry_sdk/traces.py +++ b/sentry_sdk/traces.py @@ -668,7 +668,7 @@ def __init__( self._start() def __repr__(self) -> str: - return f"<{self.__class__.__name__}(sampled={self.sampled})>" + return f"<{self.__class__.__name__}(name={self.name}, sampled={self.sampled})>" def __enter__(self) -> "NoOpStreamedSpan": return self diff --git a/tests/tracing/test_integration_tests.py b/tests/tracing/test_integration_tests.py index 99f37bf8e5..97f253bcaf 100644 --- a/tests/tracing/test_integration_tests.py +++ b/tests/tracing/test_integration_tests.py @@ -7,57 +7,14 @@ import pytest import sentry_sdk -from sentry_sdk import ( - capture_message, - continue_trace, - start_span, - start_transaction, -) +from sentry_sdk import capture_message from sentry_sdk.consts import SPANSTATUS from sentry_sdk.transport import Transport from tests.conftest import TestTransportWithOptions @pytest.mark.parametrize("sample_rate", [0.0, 1.0]) -def test_basic(sentry_init, capture_events, sample_rate): - sentry_init(traces_sample_rate=sample_rate) - events = capture_events() - - with start_transaction(name="hi") as transaction: - transaction.set_status(SPANSTATUS.OK) - with pytest.raises(ZeroDivisionError): - with start_span(op="foo", name="foodesc"): - 1 / 0 - - with start_span(op="bar", name="bardesc"): - pass - - if sample_rate: - assert len(events) == 1 - event = events[0] - - assert event["transaction"] == "hi" - assert event["transaction_info"]["source"] == "custom" - - span1, span2 = event["spans"] - parent_span = event - assert span1["status"] == "internal_error" - assert span1["tags"]["status"] == "internal_error" - assert span1["op"] == "foo" - assert span1["description"] == "foodesc" - assert "status" not in span2 - assert "status" not in span2.get("tags", {}) - assert span2["op"] == "bar" - assert span2["description"] == "bardesc" - assert parent_span["transaction"] == "hi" - assert "status" not in event["tags"] - assert event["contexts"]["trace"]["status"] == "ok" - else: - assert not events - - -@pytest.mark.parametrize("sample_rate", [0.0, 1.0]) -def test_basic_span_streaming(sentry_init, capture_items, sample_rate): +def test_basic(sentry_init, capture_items, sample_rate): sentry_init(traces_sample_rate=sample_rate, trace_lifecycle="stream") items = capture_items() @@ -91,9 +48,7 @@ def test_basic_span_streaming(sentry_init, capture_items, sample_rate): assert not items -def test_error_event_linked_without_performance_span_streaming( - sentry_init, capture_items -): +def test_error_event_linked_without_performance(sentry_init, capture_items): sentry_init(traces_sample_rate=None, trace_lifecycle="stream") items = capture_items("event") @@ -112,104 +67,7 @@ def test_error_event_linked_without_performance_span_streaming( @pytest.mark.parametrize("parent_sampled", [True, False, None]) @pytest.mark.parametrize("sample_rate", [0.0, 1.0]) -def test_continue_trace(sentry_init, capture_envelopes, parent_sampled, sample_rate): - """ - Ensure data is actually passed along via headers, and that they are read - correctly. - """ - sentry_init(traces_sample_rate=sample_rate) - envelopes = capture_envelopes() - - # make a parent transaction (normally this would be in a different service) - with start_transaction(name="hi", sampled=True if sample_rate == 0 else None): - with start_span() as old_span: - old_span.sampled = parent_sampled - headers = dict( - sentry_sdk.get_current_scope().iter_trace_propagation_headers(old_span) - ) - headers["baggage"] = ( - "other-vendor-value-1=foo;bar;baz, " - "sentry-trace_id=771a43a4192642f0b136d5159a501700, " - "sentry-public_key=49d0f7386ad645858ae85020e393bef3, " - "sentry-sample_rate=0.01337, sentry-user_id=Amelie, " - "sentry-sample_rand=0.250000, " - "other-vendor-value-2=foo;bar;" - ) - - # child transaction, to prove that we can read 'sentry-trace' header data correctly - child_transaction = continue_trace(headers, name="WRONG") - assert child_transaction is not None - assert child_transaction.parent_sampled == parent_sampled - assert child_transaction.trace_id == old_span.trace_id - assert child_transaction.same_process_as_parent is False - assert child_transaction.parent_span_id == old_span.span_id - assert child_transaction.span_id != old_span.span_id - - baggage = child_transaction._baggage - assert baggage - assert not baggage.mutable - assert baggage.sentry_items == { - "public_key": "49d0f7386ad645858ae85020e393bef3", - "trace_id": "771a43a4192642f0b136d5159a501700", - "user_id": "Amelie", - "sample_rand": "0.250000", - "sample_rate": "0.01337", - } - - # add child transaction to the scope, to show that the captured message will - # be tagged with the trace id (since it happens while the transaction is - # open) - with start_transaction(child_transaction): - # change the transaction name from "WRONG" to make sure the change - # is reflected in the final data - sentry_sdk.get_current_scope().transaction = "ho" - capture_message("hello") - - if parent_sampled is False or (sample_rate == 0 and parent_sampled is None): - # in this case the child transaction won't be captured - trace1, message = envelopes - message_payload = message.get_event() - trace1_payload = trace1.get_transaction_event() - - assert trace1_payload["transaction"] == "hi" - else: - trace1, message, trace2 = envelopes - trace1_payload = trace1.get_transaction_event() - message_payload = message.get_event() - trace2_payload = trace2.get_transaction_event() - - assert trace1_payload["transaction"] == "hi" - assert trace2_payload["transaction"] == "ho" - - assert ( - trace1_payload["contexts"]["trace"]["trace_id"] - == trace2_payload["contexts"]["trace"]["trace_id"] - == child_transaction.trace_id - == message_payload["contexts"]["trace"]["trace_id"] - ) - - if parent_sampled is not None: - expected_sample_rate = str(float(parent_sampled)) - else: - expected_sample_rate = str(sample_rate) - - assert trace2.headers["trace"] == baggage.dynamic_sampling_context() - assert trace2.headers["trace"] == { - "public_key": "49d0f7386ad645858ae85020e393bef3", - "trace_id": "771a43a4192642f0b136d5159a501700", - "user_id": "Amelie", - "sample_rand": "0.250000", - "sample_rate": expected_sample_rate, - } - - assert message_payload["message"] == "hello" - - -@pytest.mark.parametrize("parent_sampled", [True, False, None]) -@pytest.mark.parametrize("sample_rate", [0.0, 1.0]) -def test_continue_trace_span_streaming( - sentry_init, capture_items, parent_sampled, sample_rate -): +def test_continue_trace(sentry_init, capture_items, parent_sampled, sample_rate): """ Ensure data is actually passed along via headers, and that they are read correctly. @@ -333,66 +191,6 @@ def test_continue_trace_span_streaming( @pytest.mark.parametrize("sample_rate", [0.5, 1.0]) def test_dynamic_sampling_head_sdk_creates_dsc( sentry_init, capture_envelopes, sample_rate, monkeypatch -): - sentry_init(traces_sample_rate=sample_rate, release="foo") - envelopes = capture_envelopes() - - # make sure transaction is sampled for both cases - with mock.patch("sentry_sdk.tracing_utils.Random.randrange", return_value=250000): - transaction = continue_trace({}, name="Head SDK tx") - - baggage = transaction._baggage - assert baggage is None - - with start_transaction(transaction): - with start_span(op="foo", name="foodesc"): - pass - - # finish will create a new baggage entry - baggage = transaction._baggage - trace_id = transaction.trace_id - - assert baggage - assert not baggage.mutable - assert baggage.third_party_items == "" - assert baggage.sentry_items == { - "environment": "production", - "release": "foo", - "sample_rate": str(sample_rate), - "sampled": "true" if transaction.sampled else "false", - "sample_rand": "0.250000", - "transaction": "Head SDK tx", - "trace_id": trace_id, - } - - expected_baggage = ( - "sentry-trace_id=%s," - "sentry-sample_rand=0.250000," - "sentry-environment=production," - "sentry-release=foo," - "sentry-transaction=Head%%20SDK%%20tx," - "sentry-sample_rate=%s," - "sentry-sampled=%s" - % (trace_id, sample_rate, "true" if transaction.sampled else "false") - ) - assert baggage.serialize() == expected_baggage - - (envelope,) = envelopes - assert envelope.headers["trace"] == baggage.dynamic_sampling_context() - assert envelope.headers["trace"] == { - "environment": "production", - "release": "foo", - "sample_rate": str(sample_rate), - "sample_rand": "0.250000", - "sampled": "true" if transaction.sampled else "false", - "transaction": "Head SDK tx", - "trace_id": trace_id, - } - - -@pytest.mark.parametrize("sample_rate", [0.5, 1.0]) -def test_dynamic_sampling_head_sdk_creates_dsc_span_streaming( - sentry_init, capture_envelopes, sample_rate, monkeypatch ): sentry_init( traces_sample_rate=sample_rate, @@ -453,40 +251,11 @@ def test_dynamic_sampling_head_sdk_creates_dsc_span_streaming( } -@pytest.mark.parametrize( - "args,expected_refcount", - [({"traces_sample_rate": 1.0}, 100), ({"traces_sample_rate": 0.0}, 0)], -) -def test_memory_usage(sentry_init, capture_events, args, expected_refcount): - sentry_init(**args) - - references = weakref.WeakSet() - - with start_transaction(name="hi"): - for i in range(100): - with start_span(op="helloworld", name="hi {}".format(i)) as span: - - def foo(): - pass - - references.add(foo) - span.set_tag("foo", foo) - pass - - del foo - del span - - # required only for pypy (cpython frees immediately) - gc.collect() - - assert len(references) == expected_refcount - - @pytest.mark.parametrize( "args", [{"traces_sample_rate": 1.0}, {"traces_sample_rate": 0.0}], ) -def test_memory_usage_span_streaming(sentry_init, capture_events, args): +def test_memory_usage(sentry_init, capture_events, args): sentry_init(**args, trace_lifecycle="stream") references = weakref.WeakSet() @@ -512,19 +281,6 @@ def foo(): assert len(references) == 0 -def test_transactions_do_not_go_through_before_send(sentry_init, capture_events): - def before_send(event, hint): - raise RuntimeError("should not be called") - - sentry_init(traces_sample_rate=1.0, before_send=before_send) - events = capture_events() - - with start_transaction(name="/"): - pass - - assert len(events) == 1 - - def test_segments_do_not_go_through_before_send(sentry_init, capture_items): def before_send(event, hint): raise RuntimeError("should not be called") @@ -544,26 +300,7 @@ def before_send(event, hint): assert len(items) == 1 -def test_start_span_after_finish(sentry_init, capture_events): - class CustomTransport(Transport): - def capture_envelope(self, envelope): - pass - - def capture_event(self, event): - start_span(op="toolate", name="justdont") - pass - - sentry_init(traces_sample_rate=1, transport=CustomTransport()) - events = capture_events() - - with start_transaction(name="hi"): - with start_span(op="bar", name="bardesc"): - pass - - assert len(events) == 1 - - -def test_start_span_after_finish_span_streaming(sentry_init, capture_items): +def test_start_span_after_finish(sentry_init, capture_items): class CustomTransport(Transport): def capture_envelope(self, envelope): with sentry_sdk.traces.start_span(name="toolate"): @@ -591,30 +328,6 @@ def capture_event(self, event): def test_trace_propagation_meta_head_sdk(sentry_init): - sentry_init(traces_sample_rate=1.0, release="foo") - - transaction = continue_trace({}, name="Head SDK tx") - meta = None - span = None - - with start_transaction(transaction): - with start_span(op="foo", name="foodesc") as current_span: - span = current_span - meta = sentry_sdk.get_current_scope().trace_propagation_meta() - - ind = meta.find(">") + 1 - sentry_trace, baggage = meta[:ind], meta[ind:] - - assert 'meta name="sentry-trace"' in sentry_trace - sentry_trace_content = re.findall('content="([^"]*)"', sentry_trace)[0] - assert sentry_trace_content == span.to_traceparent() - - assert 'meta name="baggage"' in baggage - baggage_content = re.findall('content="([^"]*)"', baggage)[0] - assert baggage_content == transaction.get_baggage().serialize() - - -def test_trace_propagation_meta_head_sdk_span_streaming(sentry_init): sentry_init( traces_sample_rate=1.0, release="foo", @@ -650,34 +363,6 @@ def test_trace_propagation_meta_head_sdk_span_streaming(sentry_init): ], ) def test_non_error_exceptions( - sentry_init, capture_events, exception_cls, exception_value -): - sentry_init(traces_sample_rate=1.0) - events = capture_events() - - with start_transaction(name="hi") as transaction: - transaction.set_status(SPANSTATUS.OK) - with pytest.raises(exception_cls): - with start_span(op="foo", name="foodesc"): - raise exception_cls(exception_value) - - assert len(events) == 1 - event = events[0] - - span = event["spans"][0] - assert "status" not in span - assert "status" not in span.get("tags", {}) - assert "status" not in event["tags"] - assert event["contexts"]["trace"]["status"] == "ok" - - -@pytest.mark.parametrize( - "exception_cls,exception_value", - [ - (SystemExit, 0), - ], -) -def test_non_error_exceptions_span_streaming( sentry_init, capture_items, exception_cls, exception_value ): sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream") @@ -699,35 +384,7 @@ def test_non_error_exceptions_span_streaming( @pytest.mark.parametrize("exception_value", [None, 0, False]) -def test_good_sysexit_doesnt_fail_transaction( - sentry_init, capture_events, exception_value -): - sentry_init(traces_sample_rate=1.0) - events = capture_events() - - with start_transaction(name="hi") as transaction: - transaction.set_status(SPANSTATUS.OK) - with pytest.raises(SystemExit): - with start_span(op="foo", name="foodesc"): - if exception_value is not False: - sys.exit(exception_value) - else: - sys.exit() - - assert len(events) == 1 - event = events[0] - - span = event["spans"][0] - assert "status" not in span - assert "status" not in span.get("tags", {}) - assert "status" not in event["tags"] - assert event["contexts"]["trace"]["status"] == "ok" - - -@pytest.mark.parametrize("exception_value", [None, 0, False]) -def test_good_sysexit_doesnt_fail_segment_span_streaming( - sentry_init, capture_items, exception_value -): +def test_good_sysexit_doesnt_fail_segment(sentry_init, capture_items, exception_value): sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream") items = capture_items() @@ -767,58 +424,6 @@ def test_continue_trace_strict_trace_continuation( baggage_org_id, dsn_org_id, should_continue_trace, -): - sentry_init( - dsn=f"https://mysecret@{dsn_org_id}.ingest.sentry.io/12312012", - strict_trace_continuation=strict_trace_continuation, - traces_sample_rate=1.0, - transport=TestTransportWithOptions, - ) - - headers = { - "sentry-trace": "771a43a4192642f0b136d5159a501700-1234567890abcdef-1", - "baggage": ( - "other-vendor-value-1=foo;bar;baz," - "sentry-trace_id=771a43a4192642f0b136d5159a501700," - f"{baggage_org_id}," - "sentry-public_key=49d0f7386ad645858ae85020e393bef3," - "sentry-sample_rate=0.01337," - "sentry-user_id=Am%C3%A9lie," - "other-vendor-value-2=foo;bar;" - ), - } - - transaction = continue_trace(headers, name="strict trace") - - if should_continue_trace: - assert transaction.trace_id == "771a43a4192642f0b136d5159a501700" - assert transaction.parent_span_id == "1234567890abcdef" - assert transaction.parent_sampled - else: - assert transaction.trace_id != "771a43a4192642f0b136d5159a501700" - assert transaction.parent_span_id != "1234567890abcdef" - assert transaction.parent_sampled is None - - -@pytest.mark.parametrize( - "strict_trace_continuation,baggage_org_id,dsn_org_id,should_continue_trace", - ( - (True, "sentry-org_id=1234", "o1234", True), - (True, "sentry-org_id=1234", "o9999", False), - (True, "sentry-org_id=9999", "o1234", False), - (False, "sentry-org_id=1234", "o1234", True), - (False, "sentry-org_id=9999", "o1234", False), - (False, "sentry-org_id=1234", "o9999", False), - (False, "sentry-org_id=1234", "not_org_id", True), - (False, "", "o1234", True), - ), -) -def test_continue_trace_strict_trace_continuation_span_streaming( - sentry_init, - strict_trace_continuation, - baggage_org_id, - dsn_org_id, - should_continue_trace, ): sentry_init( dsn=f"https://mysecret@{dsn_org_id}.ingest.sentry.io/12312012", @@ -857,18 +462,7 @@ def test_continue_trace_strict_trace_continuation_span_streaming( assert segment._parent_sampled is None -def test_continue_trace_forces_new_traces_when_no_propagation(sentry_init): - """This is to make sure we don't have a long running trace because of TWP logic for the no propagation case.""" - - sentry_init(traces_sample_rate=1.0) - - tx1 = continue_trace({}, name="tx1") - tx2 = continue_trace({}, name="tx2") - - assert tx1.trace_id != tx2.trace_id - - -def test_continue_trace_forces_new_traces_when_no_propagation_span_streaming( +def test_continue_trace_forces_new_traces_when_no_propagation( sentry_init, ): """This is to make sure we don't have a long running trace because of TWP logic for the no propagation case.""" @@ -886,7 +480,7 @@ def test_continue_trace_forces_new_traces_when_no_propagation_span_streaming( assert segment1.trace_id != segment2.trace_id -def test_continue_trace_forces_new_traces_when_no_propagation_with_new_trace_span_streaming( +def test_continue_trace_forces_new_traces_when_no_propagation_with_new_trace( sentry_init, ): sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream") diff --git a/tests/tracing/test_misc.py b/tests/tracing/test_misc.py index 873abdbec2..3c25506224 100644 --- a/tests/tracing/test_misc.py +++ b/tests/tracing/test_misc.py @@ -117,7 +117,7 @@ def test_should_propagate_trace_to_sentry( assert should_propagate_trace(client, url) == expected_propagation_decision -def test_start_transaction_updates_scope_name(sentry_init): +def test_start_span_segment_updates_scope_name(sentry_init): sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream") scope = sentry_sdk.get_current_scope() diff --git a/tests/tracing/test_sampling.py b/tests/tracing/test_sampling.py index 0685498d13..f0b79a8a78 100644 --- a/tests/tracing/test_sampling.py +++ b/tests/tracing/test_sampling.py @@ -5,24 +5,10 @@ import pytest import sentry_sdk -from sentry_sdk import capture_exception, start_span, start_transaction -from sentry_sdk.tracing_utils import Baggage +from sentry_sdk import capture_exception from sentry_sdk.utils import logger -def test_sampling_decided_only_for_transactions(sentry_init, capture_events): - sentry_init(traces_sample_rate=0.5) - - with start_transaction(name="hi") as transaction: - assert transaction.sampled is not None - - with start_span() as span: - assert span.sampled == transaction.sampled - - with start_span() as span: - assert span.sampled is None - - def test_sampling_decided_only_for_segments(sentry_init, capture_events): sentry_init( traces_sample_rate=0.5, @@ -36,32 +22,7 @@ def test_sampling_decided_only_for_segments(sentry_init, capture_events): assert span.sampled == segment.sampled -@pytest.mark.parametrize("sampled", [True, False]) -def test_nested_transaction_sampling_override(sentry_init, sampled): - sentry_init(traces_sample_rate=1.0) - - with start_transaction(name="outer", sampled=sampled) as outer_transaction: - assert outer_transaction.sampled is sampled - with start_transaction( - name="inner", sampled=(not sampled) - ) as inner_transaction: - assert inner_transaction.sampled is not sampled - assert outer_transaction.sampled is sampled - - -def test_no_double_sampling(sentry_init, capture_events): - # Transactions should not be subject to the global/error sample rate. - # Only the traces_sample_rate should apply. - sentry_init(traces_sample_rate=1.0, sample_rate=0.0) - events = capture_events() - - with start_transaction(name="/"): - pass - - assert len(events) == 1 - - -def test_no_double_sampling_span_streaming(sentry_init, capture_items): +def test_no_double_sampling(sentry_init, capture_items): # Segments should not be subject to the global/error sample rate. # Only the traces_sample_rate should apply. sentry_init( @@ -80,17 +41,27 @@ def test_no_double_sampling_span_streaming(sentry_init, capture_items): @pytest.mark.parametrize("sampling_decision", [True, False]) -def test_get_transaction_and_span_from_scope_regardless_of_sampling_decision( +def test_get_span_from_scope_regardless_of_sampling_decision( sentry_init, sampling_decision ): - sentry_init(traces_sample_rate=1.0) + sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream") - with start_transaction(name="/", sampled=sampling_decision): - with start_span(op="child-span"): - with start_span(op="child-child-span"): + sentry_sdk.traces.continue_trace( + { + "sentry-trace": f"0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-{int(sampling_decision)}" + } + ) + + with sentry_sdk.traces.start_span(name="/"): + with sentry_sdk.traces.start_span(name="child-span"): + with sentry_sdk.traces.start_span(name="child-child-span"): scope = sentry_sdk.get_current_scope() - assert scope.span.op == "child-child-span" - assert scope.transaction.name == "/" + if sampling_decision is True: + assert scope.streamed_span.name == "child-child-span" + else: + # noop spans are not set on the scope unless they're segments + assert scope.streamed_span.name == "/" + assert scope._transaction == "/" @pytest.mark.parametrize( @@ -101,22 +72,6 @@ def test_uses_traces_sample_rate_correctly( sentry_init, traces_sample_rate, expected_decision, -): - sentry_init(traces_sample_rate=traces_sample_rate) - - baggage = Baggage(sentry_items={"sample_rand": "0.500000"}) - transaction = start_transaction(name="dogpark", baggage=baggage) - assert transaction.sampled is expected_decision - - -@pytest.mark.parametrize( - "traces_sample_rate,expected_decision", - [(0.0, False), (0.25, False), (0.75, True), (1.00, True)], -) -def test_uses_traces_sample_rate_correctly_span_streaming( - sentry_init, - traces_sample_rate, - expected_decision, ): sentry_init( traces_sample_rate=traces_sample_rate, @@ -142,22 +97,6 @@ def test_uses_traces_sampler_return_value_correctly( sentry_init, traces_sampler_return_value, expected_decision, -): - sentry_init(traces_sampler=mock.Mock(return_value=traces_sampler_return_value)) - - baggage = Baggage(sentry_items={"sample_rand": "0.500000"}) - transaction = start_transaction(name="dogpark", baggage=baggage) - assert transaction.sampled is expected_decision - - -@pytest.mark.parametrize( - "traces_sampler_return_value,expected_decision", - [(0.0, False), (0.25, False), (0.75, True), (1.00, True)], -) -def test_uses_traces_sampler_return_value_correctly_span_streaming( - sentry_init, - traces_sampler_return_value, - expected_decision, ): sentry_init( traces_sampler=mock.Mock(return_value=traces_sampler_return_value), @@ -178,16 +117,6 @@ def test_uses_traces_sampler_return_value_correctly_span_streaming( @pytest.mark.parametrize("traces_sampler_return_value", [True, False]) def test_tolerates_traces_sampler_returning_a_boolean( sentry_init, traces_sampler_return_value -): - sentry_init(traces_sampler=mock.Mock(return_value=traces_sampler_return_value)) - - transaction = start_transaction(name="dogpark") - assert transaction.sampled is traces_sampler_return_value - - -@pytest.mark.parametrize("traces_sampler_return_value", [True, False]) -def test_tolerates_traces_sampler_returning_a_boolean_span_streaming( - sentry_init, traces_sampler_return_value ): sentry_init( traces_sampler=mock.Mock(return_value=traces_sampler_return_value), @@ -198,25 +127,6 @@ def test_tolerates_traces_sampler_returning_a_boolean_span_streaming( assert span.sampled is traces_sampler_return_value -@pytest.mark.parametrize( - "traces_sample_rate,expected_decision", - [(0.0, False), (0.25, False), (0.75, True), (1.00, True)], -) -def test_traces_sampler_raising_falls_back_to_traces_sample_rate( - sentry_init, - traces_sample_rate, - expected_decision, -): - sentry_init( - traces_sampler=mock.Mock(side_effect=ValueError("boom")), - traces_sample_rate=traces_sample_rate, - ) - - baggage = Baggage(sentry_items={"sample_rand": "0.500000"}) - transaction = start_transaction(name="dogpark", baggage=baggage) - assert transaction.sampled is expected_decision - - @pytest.mark.parametrize("parent_sampling_decision", [True, False]) def test_traces_sampler_raising_falls_back_to_parent_sampling_decision( sentry_init, parent_sampling_decision @@ -226,20 +136,24 @@ def test_traces_sampler_raising_falls_back_to_parent_sampling_decision( sentry_init( traces_sampler=mock.Mock(side_effect=ValueError("boom")), traces_sample_rate=0.0 if parent_sampling_decision else 1.0, + trace_lifecycle="stream", ) - baggage = Baggage(sentry_items={"sample_rand": "0.500000"}) - transaction = start_transaction( - name="dogpark", baggage=baggage, parent_sampled=parent_sampling_decision + sentry_sdk.traces.continue_trace( + { + "sentry-trace": f"0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-{int(parent_sampling_decision)}" + } ) - assert transaction.sampled is parent_sampling_decision + + with sentry_sdk.traces.start_span(name="dogpark") as span: + assert span.sampled is parent_sampling_decision @pytest.mark.parametrize( "traces_sample_rate,expected_decision", [(0.0, False), (0.25, False), (0.75, True), (1.00, True)], ) -def test_traces_sampler_raising_falls_back_to_traces_sample_rate_span_streaming( +def test_traces_sampler_raising_falls_back_to_traces_sample_rate( sentry_init, traces_sample_rate, expected_decision, @@ -265,7 +179,7 @@ def test_traces_sampler_raising_falls_back_to_traces_sample_rate_span_streaming( "traces_sample_rate,expected_decision", [(0.0, False), (0.25, False), (0.75, True), (1.00, True)], ) -def test_traces_sampler_raising_no_incoming_trace_falls_back_to_traces_sample_rate_span_streaming( +def test_traces_sampler_raising_no_incoming_trace_falls_back_to_traces_sample_rate( sentry_init, traces_sample_rate, expected_decision, @@ -288,17 +202,6 @@ def test_traces_sampler_raising_no_incoming_trace_falls_back_to_traces_sample_ra def test_traces_sampler_raising_no_incoming_trace_and_no_traces_sample_rate( sentry_init, -): - sentry_init( - traces_sampler=mock.Mock(side_effect=ValueError("boom")), - ) - - transaction = start_transaction(name="dogpark") - assert transaction.sampled is False - - -def test_traces_sampler_raising_no_incoming_trace_and_no_traces_sample_rate_span_streaming( - sentry_init, ): sentry_init( traces_sampler=mock.Mock(side_effect=ValueError("boom")), @@ -310,20 +213,7 @@ def test_traces_sampler_raising_no_incoming_trace_and_no_traces_sample_rate_span @pytest.mark.parametrize("sampling_decision", [True, False]) -def test_only_captures_transaction_when_sampled_is_true( - sentry_init, sampling_decision, capture_events -): - sentry_init(traces_sampler=mock.Mock(return_value=sampling_decision)) - events = capture_events() - - transaction = start_transaction(name="dogpark") - transaction.finish() - - assert len(events) == (1 if sampling_decision else 0) - - -@pytest.mark.parametrize("sampling_decision", [True, False]) -def test_only_captures_segment_when_sampled_is_true_span_streaming( +def test_only_captures_segment_when_sampled_is_true( sentry_init, sampling_decision, capture_items ): sentry_init( @@ -349,27 +239,6 @@ def test_prefers_traces_sampler_to_traces_sample_rate( sentry_init, traces_sample_rate, traces_sampler_return_value, -): - # make traces_sample_rate imply the opposite of traces_sampler, to prove - # that traces_sampler takes precedence - traces_sampler = mock.Mock(return_value=traces_sampler_return_value) - sentry_init( - traces_sample_rate=traces_sample_rate, - traces_sampler=traces_sampler, - ) - - transaction = start_transaction(name="dogpark") - assert traces_sampler.called is True - assert transaction.sampled is traces_sampler_return_value - - -@pytest.mark.parametrize( - "traces_sample_rate,traces_sampler_return_value", [(0, True), (1, False)] -) -def test_prefers_traces_sampler_to_traces_sample_rate_span_streaming( - sentry_init, - traces_sample_rate, - traces_sampler_return_value, ): # make traces_sample_rate imply the opposite of traces_sampler, to prove # that traces_sampler takes precedence @@ -385,23 +254,8 @@ def test_prefers_traces_sampler_to_traces_sample_rate_span_streaming( assert span.sampled is traces_sampler_return_value -@pytest.mark.parametrize("parent_sampling_decision", [True, False]) -def test_ignores_inherited_sample_decision_when_traces_sampler_defined( - sentry_init, parent_sampling_decision -): - # make traces_sampler pick the opposite of the inherited decision, to prove - # that traces_sampler takes precedence - traces_sampler = mock.Mock(return_value=not parent_sampling_decision) - sentry_init(traces_sampler=traces_sampler) - - transaction = start_transaction( - name="dogpark", parent_sampled=parent_sampling_decision - ) - assert transaction.sampled is not parent_sampling_decision - - @pytest.mark.parametrize("parent_sampling_decision", ["1", "0"]) -def test_ignores_inherited_sample_decision_when_traces_sampler_defined_span_streaming( +def test_ignores_inherited_sample_decision_when_traces_sampler_defined( sentry_init, parent_sampling_decision ): # make traces_sampler pick the opposite of the inherited decision, to prove @@ -421,38 +275,8 @@ def test_ignores_inherited_sample_decision_when_traces_sampler_defined_span_stre assert span.sampled is not bool(int(parent_sampling_decision)) -@pytest.mark.parametrize("explicit_decision", [True, False]) -def test_traces_sampler_doesnt_overwrite_explicitly_passed_sampling_decision( - sentry_init, explicit_decision -): - # make traces_sampler pick the opposite of the explicit decision, to prove - # that the explicit decision takes precedence - traces_sampler = mock.Mock(return_value=not explicit_decision) - sentry_init(traces_sampler=traces_sampler) - - transaction = start_transaction(name="dogpark", sampled=explicit_decision) - assert transaction.sampled is explicit_decision - - -@pytest.mark.parametrize("parent_sampling_decision", [True, False]) -def test_inherits_parent_sampling_decision_when_traces_sampler_undefined( - sentry_init, parent_sampling_decision -): - # make sure the parent sampling decision is the opposite of what - # traces_sample_rate would produce, to prove the inheritance takes - # precedence - sentry_init(traces_sample_rate=0.5) - mock_random_value = 0.25 if parent_sampling_decision is False else 0.75 - - with mock.patch.object(random, "random", return_value=mock_random_value): - transaction = start_transaction( - name="dogpark", parent_sampled=parent_sampling_decision - ) - assert transaction.sampled is parent_sampling_decision - - @pytest.mark.parametrize("parent_sampling_decision", ["1", "0"]) -def test_inherits_parent_sampling_decision_when_traces_sampler_undefined_span_streaming( +def test_inherits_parent_sampling_decision_when_traces_sampler_undefined( sentry_init, parent_sampling_decision ): sentry_init( @@ -475,48 +299,6 @@ def test_inherits_parent_sampling_decision_when_traces_sampler_undefined_span_st assert span.sampled is bool(int(parent_sampling_decision)) -@pytest.mark.parametrize("parent_sampling_decision", [True, False]) -def test_passes_parent_sampling_decision_in_sampling_context( - sentry_init, parent_sampling_decision -): - sentry_init(traces_sample_rate=1.0) - - sentry_trace_header = ( - "12312012123120121231201212312012-1121201211212012-{sampled}".format( - sampled=int(parent_sampling_decision) - ) - ) - - transaction = sentry_sdk.continue_trace( - {"sentry-trace": sentry_trace_header}, - name="dogpark", - ) - - def mock_set_initial_sampling_decision(_, sampling_context): - assert "parent_sampled" in sampling_context - assert sampling_context["parent_sampled"] is parent_sampling_decision - - with mock.patch( - "sentry_sdk.tracing.Transaction._set_initial_sampling_decision", - mock_set_initial_sampling_decision, - ): - start_transaction(transaction=transaction) - - -def test_passes_custom_sampling_context_from_start_transaction_to_traces_sampler( - sentry_init, - DictionaryContaining, # noqa: N803 -): - traces_sampler = mock.Mock() - sentry_init(traces_sampler=traces_sampler) - - start_transaction(custom_sampling_context={"dogs": "yes", "cats": "maybe"}) - - traces_sampler.assert_any_call( - DictionaryContaining({"dogs": "yes", "cats": "maybe"}) - ) - - def test_custom_sampling_context(sentry_init): class MyClass: ... @@ -599,29 +381,6 @@ def test_warns_and_sets_sampled_to_false_on_invalid_traces_sampler_return_value( sentry_init, traces_sampler_return_value, StringContaining, # noqa: N803 -): - sentry_init(traces_sampler=mock.Mock(return_value=traces_sampler_return_value)) - - with mock.patch.object(logger, "warning", mock.Mock()): - transaction = start_transaction(name="dogpark") - logger.warning.assert_any_call(StringContaining("Given sample rate is invalid")) - assert transaction.sampled is False - - -@pytest.mark.parametrize( - "traces_sampler_return_value", - [ - "dogs are great", # wrong type - None, # wrong type - float("NaN"), # wrong type (edge: float, but not a valid rate) - -1.121, # wrong value - 1.231, # wrong value - ], -) -def test_warns_and_sets_sampled_to_false_on_invalid_traces_sampler_return_value_span_streaming( - sentry_init, - traces_sampler_return_value, - StringContaining, # noqa: N803 ): sentry_init( traces_sampler=mock.Mock(return_value=traces_sampler_return_value), @@ -637,11 +396,11 @@ def test_warns_and_sets_sampled_to_false_on_invalid_traces_sampler_return_value_ @pytest.mark.parametrize( "traces_sample_rate,sampled_output,expected_record_lost_event_calls", [ - (None, False, []), + (None, None, []), ( 0.0, False, - [("sample_rate", "transaction", None, 1), ("sample_rate", "span", None, 1)], + [("sample_rate", "span", None, 1)], ), (1.0, True, []), ], @@ -653,12 +412,12 @@ def test_records_lost_event_only_if_traces_sample_rate_enabled( sampled_output, expected_record_lost_event_calls, ): - sentry_init(traces_sample_rate=traces_sample_rate) + sentry_init(traces_sample_rate=traces_sample_rate, trace_lifecycle="stream") record_lost_event_calls = capture_record_lost_event_calls() - transaction = start_transaction(name="dogpark") - assert transaction.sampled is sampled_output - transaction.finish() + span = sentry_sdk.traces.start_span(name="dogpark") + assert span.sampled is sampled_output + span.end() # Use Counter because order of calls does not matter assert Counter(record_lost_event_calls) == Counter(expected_record_lost_event_calls) @@ -667,11 +426,11 @@ def test_records_lost_event_only_if_traces_sample_rate_enabled( @pytest.mark.parametrize( "traces_sampler,sampled_output,expected_record_lost_event_calls", [ - (None, False, []), + (None, None, []), ( lambda _x: 0.0, False, - [("sample_rate", "transaction", None, 1), ("sample_rate", "span", None, 1)], + [("sample_rate", "span", None, 1)], ), (lambda _x: 1.0, True, []), ], @@ -683,12 +442,12 @@ def test_records_lost_event_only_if_traces_sampler_enabled( sampled_output, expected_record_lost_event_calls, ): - sentry_init(traces_sampler=traces_sampler) + sentry_init(traces_sampler=traces_sampler, trace_lifecycle="stream") record_lost_event_calls = capture_record_lost_event_calls() - transaction = start_transaction(name="dogpark") - assert transaction.sampled is sampled_output - transaction.finish() + segment = sentry_sdk.traces.start_span(name="dogpark") + assert segment.sampled is sampled_output + segment.end() # Use Counter because order of calls does not matter assert Counter(record_lost_event_calls) == Counter(expected_record_lost_event_calls)