Skip to content

Commit 61074b3

Browse files
authored
enable node span events system tests (#2539)
* enable node span events system tests
1 parent 2084568 commit 61074b3

File tree

2 files changed

+64
-13
lines changed

2 files changed

+64
-13
lines changed

tests/parametric/test_otel_span_methods.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ def test_otel_span_extended_reserved_attributes_overrides_analytics_event(
838838
@missing_feature(context.library == "php", reason="Not implemented")
839839
@missing_feature(context.library == "java", reason="Not implemented")
840840
@missing_feature(context.library == "ruby", reason="Not implemented")
841-
@missing_feature(context.library == "nodejs", reason="Not implemented")
841+
@missing_feature(context.library < "nodejs@5.17.0", reason="Implemented in v5.17.0 & v4.41.0")
842842
@missing_feature(context.library == "dotnet", reason="Not implemented")
843843
@missing_feature(context.library < "[email protected]", reason="Not implemented")
844844
def test_otel_add_event_meta_serialization(self, test_agent, test_library):
@@ -874,7 +874,7 @@ def test_otel_add_event_meta_serialization(self, test_agent, test_library):
874874

875875
event2 = events[1]
876876
assert event2.get("name") == "second_event"
877-
assert event2.get("time_unix_nano") == event2_timestamp_ns
877+
assert event2.get("time_unix_nano") // 10000 == event2_timestamp_ns // 10000 # reduce the precision tested
878878
assert event2["attributes"].get("string_val") == "value"
879879

880880
event3 = events[2]
@@ -891,7 +891,7 @@ def test_otel_add_event_meta_serialization(self, test_agent, test_library):
891891
@missing_feature(context.library == "php", reason="Not implemented")
892892
@missing_feature(context.library == "java", reason="Not implemented")
893893
@missing_feature(context.library == "ruby", reason="Not implemented")
894-
@missing_feature(context.library == "nodejs", reason="Not implemented")
894+
@missing_feature(context.library < "nodejs@5.17.0", reason="Implemented in v5.17.0 & v4.41.0")
895895
@missing_feature(context.library < "[email protected]", reason="Not implemented")
896896
def test_otel_record_exception_does_not_set_error(self, test_agent, test_library):
897897
"""
@@ -910,7 +910,7 @@ def test_otel_record_exception_does_not_set_error(self, test_agent, test_library
910910
@missing_feature(context.library == "php", reason="Not implemented")
911911
@missing_feature(context.library == "java", reason="Not implemented")
912912
@missing_feature(context.library == "ruby", reason="Not implemented")
913-
@missing_feature(context.library == "nodejs", reason="Not implemented")
913+
@missing_feature(context.library < "nodejs@5.17.0", reason="Implemented in v5.17.0 & v4.41.0")
914914
@missing_feature(context.library == "dotnet", reason="Not implemented")
915915
@missing_feature(context.library < "[email protected]", reason="Not implemented")
916916
def test_otel_record_exception_meta_serialization(self, test_agent, test_library):
@@ -933,29 +933,64 @@ def test_otel_record_exception_meta_serialization(self, test_agent, test_library
933933

934934
events = json.loads(root_span.get("meta", {}).get("events"))
935935
assert len(events) == 3
936+
event1 = events[0]
937+
assert (
938+
event1.get("name").lower() == "exception" or "error"
939+
) # node uses error objects instead of exception objects
940+
assert event1.get("time_unix_nano") > 0
941+
942+
event2 = events[1]
943+
assert event2.get("name") == "non_exception_event"
944+
assert event2.get("time_unix_nano") > event1.get("time_unix_nano")
945+
946+
event3 = events[2]
947+
assert event3.get("name") == "exception" or "error"
948+
assert event3.get("time_unix_nano") > event2.get("time_unix_nano")
949+
950+
assert root_span["error"] == 1
951+
assert "error.type" in root_span["meta"]
952+
assert "error.stack" in root_span["meta"]
953+
954+
@missing_feature(context.library == "golang", reason="Not implemented")
955+
@missing_feature(context.library == "php", reason="Not implemented")
956+
@missing_feature(context.library == "java", reason="Not implemented")
957+
@missing_feature(context.library == "ruby", reason="Not implemented")
958+
@missing_feature(context.library == "nodejs", reason="Otel Node.js API does not support attributes")
959+
@missing_feature(context.library == "dotnet", reason="Not implemented")
960+
@missing_feature(context.library < "[email protected]", reason="Not implemented")
961+
def test_otel_record_exception_attributes_serialization(self, test_agent, test_library):
962+
"""
963+
Tests the Span.RecordException API (requires Span.AddEvent API support)
964+
and its serialization into the Datadog error tags and the 'events' tag
965+
"""
966+
with test_library:
967+
with test_library.otel_start_span("operation") as span:
968+
span.set_status(OTEL_ERROR_CODE, "error_desc")
969+
span.record_exception(
970+
message="woof1", attributes={"string_val": "value", "exception.stacktrace": "stacktrace1"}
971+
)
972+
span.add_event(name="non_exception_event", attributes={"exception.stacktrace": "non-error"})
973+
span.record_exception(message="woof3", attributes={"exception.message": "message override"})
974+
span.end_span()
936975

976+
root_span = get_span(test_agent)
977+
assert "events" in root_span["meta"]
978+
979+
events = json.loads(root_span.get("meta", {}).get("events"))
980+
assert len(events) == 3
937981
event1 = events[0]
938-
assert event1.get("name") == "exception"
939982
assert event1["attributes"].get("string_val") == "value"
940983
assert event1["attributes"].get("exception.message") == "woof1"
941984
assert event1["attributes"].get("exception.stacktrace") == "stacktrace1"
942-
assert event1.get("time_unix_nano") > 0
943985

944986
event2 = events[1]
945-
assert event2.get("name") == "non_exception_event"
946987
assert event2["attributes"].get("exception.stacktrace") == "non-error"
947-
assert event2.get("time_unix_nano") > event1.get("time_unix_nano")
948988

949989
event3 = events[2]
950-
assert event3.get("name") == "exception"
951990
assert event3["attributes"].get("exception.message") == "message override"
952-
assert event3.get("time_unix_nano") > event2.get("time_unix_nano")
953991

954-
assert root_span["error"] == 1
955992
error_message = root_span["meta"].get("error.message") or root_span["meta"].get("error.msg")
956993
assert error_message == "message override"
957-
assert "error.type" in root_span["meta"]
958-
assert "error.stack" in root_span["meta"]
959994

960995

961996
def run_operation_name_test(expected_operation_name: str, span_kind: int, attributes: dict, test_library, test_agent):

utils/build/docker/nodejs/parametric/server.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const SpanContext = require('dd-trace/packages/dd-trace/src/opentracing/span_con
99
const OtelSpanContext = require('dd-trace/packages/dd-trace/src/opentelemetry/span_context')
1010

1111
const { trace, ROOT_CONTEXT } = require('@opentelemetry/api')
12+
const { millisToHrTime } = require('@opentelemetry/core')
1213

1314
const { TracerProvider } = tracer
1415
const tracerProvider = new TracerProvider()
@@ -306,6 +307,21 @@ app.get('/trace/config', (req, res) => {
306307
});
307308
});
308309

310+
app.post("/trace/otel/add_event", (req, res) => {
311+
const { span_id, name, timestamp, attributes } = req.body;
312+
const span = otelSpans[span_id]
313+
// convert to TimeInput object using millisToHrTime
314+
span.addEvent(name, attributes, millisToHrTime(timestamp / 1000))
315+
res.json({})
316+
})
317+
318+
app.post("/trace/otel/record_exception", (req, res) => {
319+
const { span_id, message, attributes } = req.body;
320+
const span = otelSpans[span_id]
321+
span.recordException(new Error(message))
322+
res.json({})
323+
})
324+
309325
// TODO: implement this endpoint correctly, current blockers:
310326
// 1. Fails on invalid url
311327
// 2. does not generate span, because http instrumentation turned off

0 commit comments

Comments
 (0)