Skip to content

Commit b6d1609

Browse files
committed
Update parametric span events serialization tests
1 parent 07181c4 commit b6d1609

File tree

5 files changed

+138
-36
lines changed

5 files changed

+138
-36
lines changed

manifests/ruby.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ tests/:
549549
test_process_discovery.py: missing_feature
550550
test_sampling_delegation.py:
551551
Test_Decisionless_Extraction: v2.4.0
552+
test_span_events.py:
553+
Test_Span_Events: missing_feature
552554
test_span_links.py:
553555
Test_Span_Links: v2.0.0
554556
test_telemetry.py:

tests/parametric/test_span_events.py

Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import json
22
import pytest
33

4-
from utils import scenarios, missing_feature, features, rfc
4+
from utils import scenarios, features, rfc, irrelevant
55
from utils.parametric.spec.trace import find_span, find_trace
66

77

88
@rfc("https://docs.google.com/document/d/1cVod_VI7Yruq8U9dfMRFJd7npDu-uBpste2IB04GyaQ")
99
@scenarios.parametric
1010
@features.span_events
11-
@missing_feature(reason="Agent does not advertise native span events serialization support yet")
1211
class Test_Span_Events:
13-
def _test_span_with_event(self, _library_env, test_agent, test_library, retrieve_events):
14-
"""Test adding a span event, with attributes, to an active span."""
12+
def _test_span_with_native_event(self, _library_env, test_agent, test_library):
13+
"""Test adding a span event, with attributes, to an active span.
14+
Assumes native format where all values are serialized according to their original type.
15+
"""
1516
time0 = 123450
1617
name0 = "event_name"
1718
attributes0 = {
@@ -39,12 +40,12 @@ def _test_span_with_event(self, _library_env, test_agent, test_library, retrieve
3940
assert len(trace) == 1
4041
span = find_span(trace, s.span_id)
4142

42-
span_events = retrieve_events(span)
43+
span_events = span["span_events"]
4344

4445
assert len(span_events) == 2
4546

4647
event = span_events[0]
47-
assert event["time_unix_nano"] == time0 * 1000
48+
assert event["time_unix_nano"] == time0 * 1000000000
4849
assert event["name"] == name0
4950
assert event["attributes"].get("string") == {"type": 0, "string_value": "bar"}
5051
assert event["attributes"].get("bool") == {"type": 1, "bool_value": True}
@@ -53,61 +54,123 @@ def _test_span_with_event(self, _library_env, test_agent, test_library, retrieve
5354

5455
assert event["attributes"].get("str_arr") == {
5556
"type": 4,
56-
"array_value": {"type": 0, "string_value": ["a", "b", "c"]},
57+
"array_value": [
58+
{"type": 0, "string_value": "a"},
59+
{"type": 0, "string_value": "b"},
60+
{"type": 0, "string_value": "c"},
61+
],
5762
}
5863
assert event["attributes"].get("bool_arr") == {
5964
"type": 4,
60-
"array_value": {"type": 1, "bool_value": [True, False]},
65+
"array_value": [{"type": 1, "bool_value": True}, {"type": 1, "bool_value": False}],
6166
}
6267
assert event["attributes"].get("int_arr") == {
6368
"type": 4,
64-
"array_value": {"type": 2, "int_value": [5, 6]},
69+
"array_value": [{"type": 2, "int_value": 5}, {"type": 2, "int_value": 6}],
6570
}
6671
assert event["attributes"].get("double_arr") == {
6772
"type": 4,
68-
"array_value": {"type": 3, "double_value": [1.1, 2.2]},
73+
"array_value": [{"type": 3, "double_value": 1.1}, {"type": 3, "double_value": 2.2}],
6974
}
7075

7176
event = span_events[1]
72-
assert event["time_unix_nano"] == time1 * 1000
77+
assert event["time_unix_nano"] == time1 * 1000000000
7378
assert event["name"] == name1
7479
assert event["attributes"].get("bool") == {"type": 1, "bool_value": False}
7580
assert event["attributes"].get("int") == {"type": 2, "int_value": 0}
76-
assert isinstance(event["attributes"].get("int").get("int"), int)
81+
assert isinstance(event["attributes"].get("int").get("int_value"), int)
7782
assert event["attributes"].get("double") == {"type": 3, "double_value": 0.0}
78-
assert isinstance(event["attributes"].get("double").get("double"), float)
83+
assert isinstance(event["attributes"].get("double").get("double_value"), float)
84+
85+
def _test_span_with_meta_event(self, _library_env, test_agent, test_library):
86+
"""Test adding a span event, with attributes, to an active span.
87+
Assumes meta format where all values are strings.
88+
"""
89+
time0 = 123450
90+
name0 = "event_name"
91+
attributes0 = {
92+
"string": "bar",
93+
"bool": "true",
94+
"int": "1",
95+
"double": "2.3",
96+
"str_arr": ["a", "b", "c"],
97+
"bool_arr": ["true", "false"],
98+
"int_arr": ["5", "6"],
99+
"double_arr": ["1.1", "2.2"],
100+
}
101+
102+
time1 = 123451
103+
name1 = "other_event"
104+
attributes1 = {"bool": "false", "int": "0", "double": "0.0"}
79105

80-
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.7"}])
106+
with test_library, test_library.otel_start_span("test") as s:
107+
s.add_event(name0, timestamp=time0, attributes=attributes0)
108+
s.add_event(name1, timestamp=time1, attributes=attributes1)
109+
110+
traces = test_agent.wait_for_num_traces(1)
111+
112+
trace = find_trace(traces, s.trace_id)
113+
assert len(trace) == 1
114+
span = find_span(trace, s.span_id)
115+
116+
span_events = json.loads(span.get("meta", {}).get("events"))
117+
118+
assert len(span_events) == 2
119+
120+
event = span_events[0]
121+
assert event["time_unix_nano"] == time0 * 1000000000
122+
assert event["name"] == name0
123+
assert event["attributes"].get("string") == "bar"
124+
assert event["attributes"].get("bool") == "true"
125+
assert event["attributes"].get("int") == "1"
126+
assert event["attributes"].get("double") == "2.3"
127+
assert event["attributes"].get("str_arr") == ["a", "b", "c"]
128+
assert event["attributes"].get("bool_arr") == ["true", "false"]
129+
assert event["attributes"].get("int_arr") == ["5", "6"]
130+
assert event["attributes"].get("double_arr") == ["1.1", "2.2"]
131+
132+
event = span_events[1]
133+
assert event["time_unix_nano"] == time1 * 1000000000
134+
assert event["name"] == name1
135+
assert event["attributes"].get("bool") == "false"
136+
assert event["attributes"].get("int") == "0"
137+
assert isinstance(event["attributes"].get("int"), str)
138+
assert event["attributes"].get("double") == "0.0"
139+
assert isinstance(event["attributes"].get("double"), str)
140+
141+
@irrelevant(library="ruby", reason="Does not support v0.7")
142+
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.7", "DD_TRACE_NATIVE_SPAN_EVENTS": "1"}])
81143
def test_span_with_event_v07(self, library_env, test_agent, test_library):
82144
"""Test adding a span event in the v0.7 format, which support the native attribute representation."""
83145

84-
self._test_span_with_event(library_env, test_agent, test_library, lambda span: span["span_events"])
146+
self._test_span_with_native_event(library_env, test_agent, test_library)
85147

86-
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.4"}])
148+
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.4", "DD_TRACE_NATIVE_SPAN_EVENTS": "1"}])
87149
def test_span_with_event_v04(self, library_env, test_agent, test_library):
88150
"""Test adding a span event in the v0.4 format, which support the native attribute representation."""
89151

90-
self._test_span_with_event(library_env, test_agent, test_library, lambda span: span["span_events"])
152+
self._test_span_with_native_event(library_env, test_agent, test_library)
91153

92-
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.5"}])
154+
@irrelevant(library="ruby", reason="Does not support v0.5")
155+
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.5", "DD_TRACE_NATIVE_SPAN_EVENTS": "1"}])
93156
def test_span_with_event_v05(self, library_env, test_agent, test_library):
94157
"""Test adding a span event in the v0.5 format, which does not support the native attribute representation.
95-
Thus span events are serialized as span tags.
158+
Thus span events are serialized as span tags, and attribute values all strings.
96159
"""
97160

98-
self._test_span_with_event(
99-
library_env, test_agent, test_library, lambda span: json.loads(span.get("meta", {}).get("events"))
100-
)
161+
self._test_span_with_meta_event(library_env, test_agent, test_library)
101162

163+
@pytest.mark.parametrize("library_env", [{"DD_TRACE_API_VERSION": "v0.4", "DD_TRACE_NATIVE_SPAN_EVENTS": "1"}])
102164
def test_span_with_invalid_event_attributes(self, library_env, test_agent, test_library):
103165
"""Test adding a span event, with invalid attributes, to an active span.
104166
Span events with invalid attributes should be discarded.
167+
Valid attributes should be kept.
105168
"""
106169

107170
with test_library, test_library.dd_start_span("test") as s:
108171
s.add_event(
109172
"name",
110-
timestamp=123,
173+
time_unix_nano=123,
111174
attributes={
112175
"int": 1,
113176
"invalid_arr1": [1, "a"],

utils/_context/_scenarios/parametric.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ def ruby_library_factory() -> APMLibraryTestServer:
604604
COPY {ruby_reldir} .
605605
COPY {ruby_reldir}/../install_ddtrace.sh binaries* /binaries/
606606
COPY {ruby_reldir}/system_tests_library_version.sh system_tests_library_version.sh
607+
RUN gem install datadog
607608
RUN bundle install
608609
RUN /binaries/install_ddtrace.sh
609610
COPY {ruby_reldir}/server.rb /app/

utils/build/docker/ruby/parametric/server.rb

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,23 @@ def to_json(*_args)
459459
end
460460
end
461461

462+
class TraceSpanAddEventsArgs
463+
attr_accessor :span_id, :name, :timestamp, :attributes
464+
465+
def initialize(params)
466+
@span_id = params['span_id']
467+
@name = params['name']
468+
@timestamp = params['timestamp']
469+
@attributes = params['attributes']
470+
end
471+
end
472+
473+
class TraceSpanAddEventReturn
474+
def to_json(*_args)
475+
{}.to_json
476+
end
477+
end
478+
462479
def get_ddtrace_version
463480
Gem::Version.new(Datadog::VERSION)
464481
end
@@ -562,6 +579,8 @@ def call(env)
562579
handle_trace_span_error(req, res)
563580
when '/trace/span/add_link'
564581
handle_trace_span_add_link(req, res)
582+
when '/trace/span/add_event'
583+
handle_trace_span_add_event(req, res)
565584
when '/trace/otel/start_span'
566585
handle_trace_otel_start_span(req, res)
567586
when '/trace/otel/add_event'
@@ -708,6 +727,31 @@ def handle_trace_span_add_link(req, res)
708727
res.write(TraceSpanAddLinkReturn.new.to_json)
709728
end
710729

730+
def handle_trace_span_add_event(req, res)
731+
args = TraceSpanAddEventsArgs.new(JSON.parse(req.body.read))
732+
span = find_span(args.span_id)
733+
734+
# Create a new SpanEvent with the provided parameters
735+
event = Datadog::Tracing::SpanEvent.new(
736+
args.name,
737+
attributes: args.attributes,
738+
time_unix_nano: args.timestamp * 1000
739+
)
740+
741+
# Add the event to the span's events array
742+
span.span_events << event
743+
744+
res.write(TraceSpanAddEventReturn.new.to_json)
745+
end
746+
747+
def handle_trace_otel_add_event(req, res)
748+
args = OtelAddEventArgs.new(JSON.parse(req.body.read))
749+
750+
span = OTEL_SPANS[args.span_id]
751+
span.add_event(args.name, attributes: args.attributes, timestamp: args.timestamp)
752+
res.write(OtelAddEventReturn.new.to_json)
753+
end
754+
711755
def handle_trace_crash(_req, res)
712756
STDOUT.puts "Crashing server..."
713757
Process.kill('SEGV', Process.pid)
@@ -748,17 +792,6 @@ def handle_trace_otel_start_span(req, res)
748792
res.write(OtelStartSpanReturn.new(span_id_b10, t_id).to_json)
749793
end
750794

751-
def handle_trace_otel_add_event(req, res)
752-
args = OtelAddEventArgs.new(JSON.parse(req.body.read))
753-
span = OTEL_SPANS[args.span_id]
754-
span.add_event(
755-
args.name,
756-
timestamp: otel_correct_time(args.timestamp),
757-
attributes: args.attributes
758-
)
759-
res.write(OtelAddEventReturn.new.to_json)
760-
end
761-
762795
def handle_trace_otel_record_exception(req, res)
763796
args = OtelRecordExceptionArgs.new(JSON.parse(req.body.read))
764797

utils/parametric/_library_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,13 @@ def span_add_link(self, span_id: int, parent_id: int, attributes: dict | None =
233233
},
234234
)
235235

236-
def span_add_event(self, span_id: int, name: str, timestamp: int, attributes: dict | None = None):
236+
def span_add_event(self, span_id: int, name: str, time_unix_nano: int, attributes: dict | None = None):
237237
self._session.post(
238238
self._url("/trace/span/add_event"),
239239
json={
240240
"span_id": span_id,
241241
"name": name,
242-
"timestamp": timestamp,
242+
"timestamp": time_unix_nano,
243243
"attributes": attributes or {},
244244
},
245245
)
@@ -433,6 +433,9 @@ def set_error(self, typestr: str = "", message: str = "", stack: str = ""):
433433
def add_link(self, parent_id: int, attributes: dict | None = None):
434434
self._client.span_add_link(self.span_id, parent_id, attributes)
435435

436+
def add_event(self, name: str, time_unix_nano: int, attributes: dict | None = None):
437+
self._client.span_add_event(self.span_id, name, time_unix_nano, attributes)
438+
436439
def finish(self):
437440
self._client.finish_span(self.span_id)
438441

0 commit comments

Comments
 (0)