Skip to content

Commit 351b37e

Browse files
committed
fix(tracing): Make functions_to_trace work when streaming spans
1 parent 611584b commit 351b37e

2 files changed

Lines changed: 192 additions & 68 deletions

File tree

‎sentry_sdk/client.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
from sentry_sdk.serializer import serialize
4343
from sentry_sdk.sessions import SessionFlusher
4444
from sentry_sdk.traces import SpanStatus, StreamedSpan
45-
from sentry_sdk.tracing import trace
45+
from sentry_sdk.traces import trace as streaming_trace
46+
from sentry_sdk.tracing import trace as legacy_trace
4647
from sentry_sdk.tracing_utils import has_span_streaming_enabled
4748
from sentry_sdk.transport import (
4849
AsyncHttpTransport,
@@ -533,8 +534,14 @@ def _setup_instrumentation(
533534
self, functions_to_trace: "Sequence[Dict[str, str]]"
534535
) -> None:
535536
"""
536-
Instruments the functions given in the list `functions_to_trace` with the `@sentry_sdk.tracing.trace` decorator.
537+
Instruments the functions given in the list `functions_to_trace` with a trace decorator.
537538
"""
539+
trace = (
540+
streaming_trace
541+
if has_span_streaming_enabled(self.options)
542+
else legacy_trace
543+
)
544+
538545
for function in functions_to_trace:
539546
class_name = None
540547
function_qualname = function["qualified_name"]

‎tests/test_basics.py‎

Lines changed: 183 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,8 @@ def _hello_world(word):
843843
return "Hello, {}".format(word)
844844

845845

846-
def test_functions_to_trace(sentry_init, capture_events):
846+
@pytest.mark.parametrize("span_streaming", [True, False])
847+
def test_functions_to_trace(sentry_init, capture_events, capture_items, span_streaming):
847848
functions_to_trace = [
848849
{"qualified_name": "tests.test_basics._hello_world"},
849850
{"qualified_name": "time.sleep"},
@@ -857,27 +858,48 @@ def test_functions_to_trace(sentry_init, capture_events):
857858
sentry_init(
858859
traces_sample_rate=1.0,
859860
functions_to_trace=functions_to_trace,
861+
trace_lifecycle="stream" if span_streaming else "static",
860862
)
861863

862-
events = capture_events()
864+
if span_streaming:
865+
items = capture_items("span")
866+
else:
867+
events = capture_events()
863868

864-
with start_transaction(name="something"):
865-
time.sleep(0)
869+
if span_streaming:
870+
with sentry_sdk.traces.start_span(name="something"):
871+
time.sleep(0)
866872

867-
for word in ["World", "You"]:
868-
_hello_world(word)
869-
finally:
870-
_hello_world = original_hello_world
871-
time.sleep = original_sleep
873+
for word in ["World", "You"]:
874+
_hello_world(word)
872875

873-
assert len(events) == 1
876+
sentry_sdk.flush()
877+
spans = [item.payload for item in items]
878+
child_spans = [s for s in spans if not s.get("is_segment")]
879+
child_spans.sort(key=lambda s: s["start_timestamp"])
874880

875-
(event,) = events
881+
assert len(child_spans) == 3
882+
assert child_spans[0]["name"] == "time.sleep"
883+
assert child_spans[1]["name"] == "tests.test_basics._hello_world"
884+
assert child_spans[2]["name"] == "tests.test_basics._hello_world"
885+
else:
886+
with start_transaction(name="something"):
887+
time.sleep(0)
888+
889+
for word in ["World", "You"]:
890+
_hello_world(word)
876891

877-
assert len(event["spans"]) == 3
878-
assert event["spans"][0]["description"] == "time.sleep"
879-
assert event["spans"][1]["description"] == "tests.test_basics._hello_world"
880-
assert event["spans"][2]["description"] == "tests.test_basics._hello_world"
892+
assert len(events) == 1
893+
894+
(event,) = events
895+
896+
assert len(event["spans"]) == 3
897+
assert event["spans"][0]["description"] == "time.sleep"
898+
assert event["spans"][1]["description"] == "tests.test_basics._hello_world"
899+
assert event["spans"][2]["description"] == "tests.test_basics._hello_world"
900+
finally:
901+
_hello_world = original_hello_world
902+
time.sleep = original_sleep
881903

882904

883905
class WorldGreeter:
@@ -888,7 +910,10 @@ def greet(self, new_word=None):
888910
return "Hello, {}".format(new_word if new_word else self.word)
889911

890912

891-
def test_functions_to_trace_with_class(sentry_init, capture_events):
913+
@pytest.mark.parametrize("span_streaming", [True, False])
914+
def test_functions_to_trace_with_class(
915+
sentry_init, capture_events, capture_items, span_streaming
916+
):
892917
functions_to_trace = [
893918
{"qualified_name": "tests.test_basics.WorldGreeter.greet"},
894919
]
@@ -899,25 +924,49 @@ def test_functions_to_trace_with_class(sentry_init, capture_events):
899924
sentry_init(
900925
traces_sample_rate=1.0,
901926
functions_to_trace=functions_to_trace,
927+
trace_lifecycle="stream" if span_streaming else "static",
902928
)
903929

904-
events = capture_events()
905-
906-
with start_transaction(name="something"):
907-
wg = WorldGreeter("World")
908-
wg.greet()
909-
wg.greet("You")
930+
if span_streaming:
931+
items = capture_items("span")
932+
else:
933+
events = capture_events()
934+
935+
if span_streaming:
936+
with sentry_sdk.traces.start_span(name="something"):
937+
wg = WorldGreeter("World")
938+
wg.greet()
939+
wg.greet("You")
940+
941+
sentry_sdk.flush()
942+
spans = [item.payload for item in items]
943+
child_spans = [s for s in spans if not s.get("is_segment")]
944+
945+
assert len(child_spans) == 2
946+
assert child_spans[0]["name"] == "tests.test_basics.WorldGreeter.greet"
947+
assert child_spans[1]["name"] == "tests.test_basics.WorldGreeter.greet"
948+
else:
949+
with start_transaction(name="something"):
950+
wg = WorldGreeter("World")
951+
wg.greet()
952+
wg.greet("You")
953+
954+
assert len(events) == 1
955+
956+
(event,) = events
957+
958+
assert len(event["spans"]) == 2
959+
assert (
960+
event["spans"][0]["description"]
961+
== "tests.test_basics.WorldGreeter.greet"
962+
)
963+
assert (
964+
event["spans"][1]["description"]
965+
== "tests.test_basics.WorldGreeter.greet"
966+
)
910967
finally:
911968
WorldGreeter.greet = original_function
912969

913-
assert len(events) == 1
914-
915-
(event,) = events
916-
917-
assert len(event["spans"]) == 2
918-
assert event["spans"][0]["description"] == "tests.test_basics.WorldGreeter.greet"
919-
assert event["spans"][1]["description"] == "tests.test_basics.WorldGreeter.greet"
920-
921970

922971
def test_multiple_setup_integrations_calls():
923972
first_call_return = setup_integrations([NoOpIntegration()], with_defaults=False)
@@ -939,98 +988,166 @@ def class_(cls, arg):
939988

940989
# We need to fork here because the test modifies tests.test_basics.TracingTestClass
941990
@pytest.mark.forked
942-
def test_staticmethod_class_tracing(sentry_init, capture_events):
991+
@pytest.mark.parametrize("span_streaming", [True, False])
992+
def test_staticmethod_class_tracing(
993+
sentry_init, capture_events, capture_items, span_streaming
994+
):
943995
sentry_init(
944996
debug=True,
945997
traces_sample_rate=1.0,
946998
functions_to_trace=[
947999
{"qualified_name": "tests.test_basics.TracingTestClass.static"}
9481000
],
1001+
trace_lifecycle="stream" if span_streaming else "static",
9491002
)
9501003

951-
events = capture_events()
1004+
if span_streaming:
1005+
items = capture_items("span")
9521006

953-
with sentry_sdk.start_transaction(name="test"):
954-
assert TracingTestClass.static(1) == 1
1007+
with sentry_sdk.traces.start_span(name="test"):
1008+
assert TracingTestClass.static(1) == 1
9551009

956-
(event,) = events
957-
assert event["type"] == "transaction"
958-
assert event["transaction"] == "test"
1010+
sentry_sdk.flush()
1011+
spans = [item.payload for item in items]
1012+
child_spans = [s for s in spans if not s.get("is_segment")]
1013+
1014+
assert len(child_spans) == 1
1015+
assert child_spans[0]["name"] == "tests.test_basics.TracingTestClass.static"
1016+
else:
1017+
events = capture_events()
9591018

960-
(span,) = event["spans"]
961-
assert span["description"] == "tests.test_basics.TracingTestClass.static"
1019+
with sentry_sdk.start_transaction(name="test"):
1020+
assert TracingTestClass.static(1) == 1
1021+
1022+
(event,) = events
1023+
assert event["type"] == "transaction"
1024+
assert event["transaction"] == "test"
1025+
1026+
(span,) = event["spans"]
1027+
assert span["description"] == "tests.test_basics.TracingTestClass.static"
9621028

9631029

9641030
# We need to fork here because the test modifies tests.test_basics.TracingTestClass
9651031
@pytest.mark.forked
966-
def test_staticmethod_instance_tracing(sentry_init, capture_events):
1032+
@pytest.mark.parametrize("span_streaming", [True, False])
1033+
def test_staticmethod_instance_tracing(
1034+
sentry_init, capture_events, capture_items, span_streaming
1035+
):
9671036
sentry_init(
9681037
debug=True,
9691038
traces_sample_rate=1.0,
9701039
functions_to_trace=[
9711040
{"qualified_name": "tests.test_basics.TracingTestClass.static"}
9721041
],
1042+
trace_lifecycle="stream" if span_streaming else "static",
9731043
)
9741044

975-
events = capture_events()
1045+
if span_streaming:
1046+
items = capture_items("span")
9761047

977-
with sentry_sdk.start_transaction(name="test"):
978-
assert TracingTestClass().static(1) == 1
1048+
with sentry_sdk.traces.start_span(name="test"):
1049+
assert TracingTestClass().static(1) == 1
9791050

980-
(event,) = events
981-
assert event["type"] == "transaction"
982-
assert event["transaction"] == "test"
1051+
sentry_sdk.flush()
1052+
spans = [item.payload for item in items]
1053+
child_spans = [s for s in spans if not s.get("is_segment")]
1054+
1055+
assert len(child_spans) == 1
1056+
assert child_spans[0]["name"] == "tests.test_basics.TracingTestClass.static"
1057+
else:
1058+
events = capture_events()
1059+
1060+
with sentry_sdk.start_transaction(name="test"):
1061+
assert TracingTestClass().static(1) == 1
1062+
1063+
(event,) = events
1064+
assert event["type"] == "transaction"
1065+
assert event["transaction"] == "test"
9831066

984-
(span,) = event["spans"]
985-
assert span["description"] == "tests.test_basics.TracingTestClass.static"
1067+
(span,) = event["spans"]
1068+
assert span["description"] == "tests.test_basics.TracingTestClass.static"
9861069

9871070

9881071
# We need to fork here because the test modifies tests.test_basics.TracingTestClass
9891072
@pytest.mark.forked
990-
def test_classmethod_class_tracing(sentry_init, capture_events):
1073+
@pytest.mark.parametrize("span_streaming", [True, False])
1074+
def test_classmethod_class_tracing(
1075+
sentry_init, capture_events, capture_items, span_streaming
1076+
):
9911077
sentry_init(
9921078
debug=True,
9931079
traces_sample_rate=1.0,
9941080
functions_to_trace=[
9951081
{"qualified_name": "tests.test_basics.TracingTestClass.class_"}
9961082
],
1083+
trace_lifecycle="stream" if span_streaming else "static",
9971084
)
9981085

999-
events = capture_events()
1086+
if span_streaming:
1087+
items = capture_items("span")
10001088

1001-
with sentry_sdk.start_transaction(name="test"):
1002-
assert TracingTestClass.class_(1) == (TracingTestClass, 1)
1089+
with sentry_sdk.traces.start_span(name="test"):
1090+
assert TracingTestClass.class_(1) == (TracingTestClass, 1)
10031091

1004-
(event,) = events
1005-
assert event["type"] == "transaction"
1006-
assert event["transaction"] == "test"
1092+
sentry_sdk.flush()
1093+
spans = [item.payload for item in items]
1094+
child_spans = [s for s in spans if not s.get("is_segment")]
10071095

1008-
(span,) = event["spans"]
1009-
assert span["description"] == "tests.test_basics.TracingTestClass.class_"
1096+
assert len(child_spans) == 1
1097+
assert child_spans[0]["name"] == "tests.test_basics.TracingTestClass.class_"
1098+
else:
1099+
events = capture_events()
1100+
1101+
with sentry_sdk.start_transaction(name="test"):
1102+
assert TracingTestClass.class_(1) == (TracingTestClass, 1)
1103+
1104+
(event,) = events
1105+
assert event["type"] == "transaction"
1106+
assert event["transaction"] == "test"
1107+
1108+
(span,) = event["spans"]
1109+
assert span["description"] == "tests.test_basics.TracingTestClass.class_"
10101110

10111111

10121112
# We need to fork here because the test modifies tests.test_basics.TracingTestClass
10131113
@pytest.mark.forked
1014-
def test_classmethod_instance_tracing(sentry_init, capture_events):
1114+
@pytest.mark.parametrize("span_streaming", [True, False])
1115+
def test_classmethod_instance_tracing(
1116+
sentry_init, capture_events, capture_items, span_streaming
1117+
):
10151118
sentry_init(
10161119
debug=True,
10171120
traces_sample_rate=1.0,
10181121
functions_to_trace=[
10191122
{"qualified_name": "tests.test_basics.TracingTestClass.class_"}
10201123
],
1124+
trace_lifecycle="stream" if span_streaming else "static",
10211125
)
10221126

1023-
events = capture_events()
1127+
if span_streaming:
1128+
items = capture_items("span")
10241129

1025-
with sentry_sdk.start_transaction(name="test"):
1026-
assert TracingTestClass().class_(1) == (TracingTestClass, 1)
1130+
with sentry_sdk.traces.start_span(name="test"):
1131+
assert TracingTestClass().class_(1) == (TracingTestClass, 1)
10271132

1028-
(event,) = events
1029-
assert event["type"] == "transaction"
1030-
assert event["transaction"] == "test"
1133+
sentry_sdk.flush()
1134+
spans = [item.payload for item in items]
1135+
child_spans = [s for s in spans if not s.get("is_segment")]
1136+
1137+
assert len(child_spans) == 1
1138+
assert child_spans[0]["name"] == "tests.test_basics.TracingTestClass.class_"
1139+
else:
1140+
events = capture_events()
1141+
1142+
with sentry_sdk.start_transaction(name="test"):
1143+
assert TracingTestClass().class_(1) == (TracingTestClass, 1)
1144+
1145+
(event,) = events
1146+
assert event["type"] == "transaction"
1147+
assert event["transaction"] == "test"
10311148

1032-
(span,) = event["spans"]
1033-
assert span["description"] == "tests.test_basics.TracingTestClass.class_"
1149+
(span,) = event["spans"]
1150+
assert span["description"] == "tests.test_basics.TracingTestClass.class_"
10341151

10351152

10361153
def test_functions_to_trace_no_dot_does_not_crash(sentry_init):

0 commit comments

Comments
 (0)