Skip to content

Commit

Permalink
parametric: remove http_headers from otel start span creation (#3478)
Browse files Browse the repository at this point in the history
  • Loading branch information
mabdinur authored Nov 20, 2024
1 parent 555dbce commit d1e67f6
Show file tree
Hide file tree
Showing 18 changed files with 50 additions and 512 deletions.
2 changes: 0 additions & 2 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,6 @@ tests/:
Test_Otel_Env_Vars: v2.53.0
test_otel_span_with_baggage.py:
Test_Otel_Span_With_Baggage: missing_feature
test_otel_span_with_w3c.py:
Test_Otel_Span_With_W3c: v2.42.0
test_otel_tracer.py:
Test_Otel_Tracer: v2.8.0
test_parametric_endpoints.py:
Expand Down
2 changes: 0 additions & 2 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,6 @@ tests/:
Test_Otel_Span_Methods: v0.94.0
test_otel_span_with_baggage.py:
Test_Otel_Span_With_Baggage: missing_feature
test_otel_span_with_w3c.py:
Test_Otel_Span_With_W3c: v0.94.0
test_otel_tracer.py:
Test_Otel_Tracer: v0.94.0
test_parametric_endpoints.py:
Expand Down
2 changes: 0 additions & 2 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,6 @@ tests/:
Test_Otel_Span_Methods: v2.8.0
test_otel_span_with_baggage.py:
Test_Otel_Span_With_Baggage: missing_feature
test_otel_span_with_w3c.py:
Test_Otel_Span_With_W3c: v2.8.0
test_otel_tracer.py:
Test_Otel_Tracer: v2.8.0
test_parametric_endpoints.py:
Expand Down
2 changes: 0 additions & 2 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,6 @@ tests/:
Test_Otel_Span_Methods: v1.17.0
test_otel_span_with_baggage.py:
Test_Otel_Span_With_Baggage: missing_feature
test_otel_span_with_w3c.py:
Test_Otel_Span_With_W3c: v1.17.0
test_parametric_endpoints.py:
Test_Parametric_DDSpan_Set_Resource: missing_feature (set_resource endpoint is not supported)
Test_Parametric_DDTrace_Baggage: missing_feature (baggage is not supported)
Expand Down
23 changes: 6 additions & 17 deletions tests/parametric/test_otel_api_interoperability.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,35 +227,24 @@ def test_span_links_add(self, test_agent, test_library):
- Test that links can be added with the Datadog API on a span created with the OTel API
"""
with test_library:
with test_library.otel_start_span("otel.span") as otel_span:
current_span = test_library.current_span()
with test_library.start_span("dd_root") as dd_span:
pass

with test_library.otel_start_span("otel_root") as otel_span:
current_span = test_library.current_span()
current_span.add_link(
parent_id=0,
attributes=TEST_ATTRIBUTES,
http_headers=[
("traceparent", f"00-{TEST_TRACE_ID}-{TEST_SPAN_ID}-01"),
("tracestate", TEST_TRACESTATE),
],
parent_id=dd_span.span_id, attributes=TEST_ATTRIBUTES,
)

otel_span.end_span()

traces = test_agent.wait_for_num_traces(1, sort_by_start=False)
traces = test_agent.wait_for_num_traces(2, sort_by_start=False)
trace = find_trace(traces, otel_span.trace_id)
assert len(trace) == 1

root = find_root_span(trace)
span_links = retrieve_span_links(root)
assert len(span_links) == 1

link = span_links[0]
assert link["trace_id"] == TEST_TRACE_ID_LOW
assert link["trace_id_high"] == TEST_TRACE_ID_HIGH
assert link["span_id"] == TEST_SPAN_ID_INT
assert "t.dm:-0" in link["tracestate"]
assert link["attributes"]["arg1"] == "val1"

def test_concurrent_traces_in_order(self, test_agent, test_library):
"""
- Basic concurrent traces and spans
Expand Down
122 changes: 9 additions & 113 deletions tests/parametric/test_otel_span_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,107 +512,6 @@ def test_otel_span_started_with_link_from_another_span(self, test_agent, test_li
root_tid = root["meta"].get("_dd.p.tid", "0")
assert link.get("trace_id_high") == int(root_tid, 16)

@missing_feature(context.library < "[email protected]", reason="Will be released in 2.53.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 1.26.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 3.48.0, 4.27.0, and 5.3.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 1.61.0")
@missing_feature(context.library < "[email protected]", reason="Not implemented")
@missing_feature(context.library == "php", reason="Implemented in 0.97.0 but link.flags are not natively supported")
def test_otel_span_started_with_link_from_datadog_headers(self, test_agent, test_library):
"""Properly inject datadog distributed tracing information into span links.
"""
with test_library:
with test_library.otel_start_span(
"root",
links=[
Link(
http_headers=[
["x-datadog-trace-id", "1234567890"],
["x-datadog-parent-id", "9876543210"],
["x-datadog-sampling-priority", "2"],
["x-datadog-origin", "synthetics"],
["x-datadog-tags", "_dd.p.dm=-4,_dd.p.tid=0000000000000010"],
],
attributes={"foo": "bar"},
)
],
) as span:
span.end_span()

traces = test_agent.wait_for_num_traces(1)
trace = find_trace(traces, span.trace_id)
span = find_span(trace, span.span_id)
span_links = retrieve_span_links(span)
assert span_links is not None
assert len(span_links) == 1

link = span_links[0]
assert link.get("span_id") == 9876543210
assert link.get("trace_id") == 1234567890
assert link.get("trace_id_high") == 16

# Tracestate is not required, but if it is present, it must be valid
if link.get("tracestate"):
tracestateArr = link["tracestate"].split(",")
assert len(tracestateArr) == 1 and tracestateArr[0].startswith("dd=")
tracestateDD = tracestateArr[0][3:].split(";")
assert "o:synthetics" in tracestateDD
assert "s:2" in tracestateDD
assert "t.dm:-4" in tracestateDD
# Sampled flag should be set to match the existing tracestate
assert link.get("flags") == 1 | TRACECONTEXT_FLAGS_SET

@missing_feature(context.library < "[email protected]", reason="Will be released in 2.53.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 1.28.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 3.48.0, 4.27.0, and 5.3.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 1.61.0")
@bug(context.library < "[email protected]", reason="APMRP-360")
@missing_feature(context.library == "php", reason="Implemented in 0.97.0 but link.flags are not natively supported")
def test_otel_span_started_with_link_from_w3c_headers(self, test_agent, test_library):
"""Properly inject w3c distributed tracing information into span links.
This mostly tests that the injected tracestate and flags are accurate.
"""
with test_library:
with test_library.otel_start_span(
"root",
links=[
Link(
http_headers=[
["traceparent", "00-12345678901234567890123456789012-1234567890123456-01"],
["tracestate", "foo=1,dd=t.dm:-4;s:2,bar=baz"],
]
)
],
) as span:
span.end_span()

traces = test_agent.wait_for_num_traces(1)
trace = find_trace(traces, span.trace_id)
span = find_span(trace, span.span_id)
span_links = retrieve_span_links(span)
assert span_links is not None
assert len(span_links) == 1

link = span_links[0]
assert link.get("span_id") == 1311768467284833366
assert link.get("trace_id") == 8687463697196027922
assert link.get("trace_id_high") == 1311768467284833366

assert link.get("tracestate") is not None
tracestateArr = link["tracestate"].split(",")
dd_member = next(iter([x for x in tracestateArr if x.startswith("dd=")]), None)
foo_member = next(iter([x for x in tracestateArr if x.startswith("foo=")]), None)
bar_member = next(iter([x for x in tracestateArr if x.startswith("bar=")]), None)
# ruby removes the dd member from the tracestate while python does not
if dd_member:
assert "s:2" in dd_member
assert "t.dm:-4" in dd_member
assert foo_member == "foo=1"
assert bar_member == "bar=baz"

assert (link.get("flags") == 1 | TRACECONTEXT_FLAGS_SET) or (link.get("flags") == TRACECONTEXT_FLAGS_SET)
assert link.get("attributes") is None or len(link.get("attributes")) == 0

@missing_feature(context.library < "[email protected]", reason="Will be released in 2.53.0")
@missing_feature(context.library < "[email protected]", reason="Implemented in 1.26.0")
@missing_feature(context.library == "golang", reason="Not implemented")
Expand All @@ -623,26 +522,23 @@ def test_otel_span_link_attribute_handling(self, test_agent, test_library):
"""Test that span links implementations correctly handle attributes according to spec.
"""
with test_library:
with test_library.otel_start_span("span1") as s1:
s1.end_span()

with test_library.otel_start_span(
"root",
links=[
Link(
http_headers=[
["x-datadog-trace-id", "1234567890"],
["x-datadog-parent-id", "9876543210"],
["x-datadog-sampling-priority", "2"],
["x-datadog-origin", "synthetics"],
["x-datadog-tags", "_dd.p.dm=-4,_dd.p.tid=0000000000000010"],
],
parent_id=s1.span_id,
attributes={"foo": "bar", "array": ["a", "b", "c"], "bools": [True, False], "nested": [1, 2]},
)
],
) as span:
span.end_span()
) as s2:
s2.end_span()

traces = test_agent.wait_for_num_traces(1)
trace = find_trace(traces, span.trace_id)
span = find_span(trace, span.span_id)
traces = test_agent.wait_for_num_traces(2)
trace = find_trace(traces, s2.trace_id)
span = find_span(trace, s2.span_id)
span_links = retrieve_span_links(span)
assert span_links is not None
assert len(span_links) == 1
Expand Down
62 changes: 0 additions & 62 deletions tests/parametric/test_otel_span_with_w3c.py

This file was deleted.

61 changes: 1 addition & 60 deletions utils/build/docker/dotnet/parametric/Endpoints/ApmTestApiOtel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,34 +47,6 @@ private static async Task<string> OtelStartSpan(HttpRequest request)
}
}

// try extracting parent context from headers (remote parent)
if (requestBodyObject.TryGetValue("http_headers", out var headersList))
{
var manualExtractedContext = _spanContextExtractor.Extract(
((Newtonsoft.Json.Linq.JArray)headersList).ToObject<string[][]>(),
getter: GetHeaderValues!);

_logger?.LogInformation("Extracted SpanContext: {ExtractedContext}", manualExtractedContext);

if (manualExtractedContext is not null)
{
// This implementation is .NET v3 specific, and assumes that the span returned by StartActive is a DuckType
var extractedContext = manualExtractedContext.GetType()
.GetProperty("Instance", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
?.GetValue(manualExtractedContext);
var parentTraceId = ActivityTraceId.CreateFromString(RawTraceId.GetValue(extractedContext) as string);
var parentSpanId = ActivitySpanId.CreateFromString(RawSpanId.GetValue(extractedContext) as string);
var flags = (SamplingPriority.GetValue(extractedContext) as int?) > 0 ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None;

remoteParentContext = new ActivityContext(
parentTraceId,
parentSpanId,
flags,
AdditionalW3CTraceState.GetValue(extractedContext) as string,
isRemote: true);
}
}

// sanity check that we didn't receive both a local and remote parent
if (localParentContext != null && remoteParentContext != null)
{
Expand Down Expand Up @@ -132,38 +104,7 @@ private static async Task<string> OtelStartSpan(HttpRequest request)
tags = ToActivityTagsCollection(((Newtonsoft.Json.Linq.JObject?)spanLink["attributes"])?.ToObject<Dictionary<string, object>>());
}

ActivityContext contextToLink = new ActivityContext();

if (parentSpanLink > 0)
{
contextToLink = FindActivity(parentSpanLink).Context;
}
else
{
var httpHeadersToken = (JArray)spanLink["http_headers"]!;

var manualExtractedContext = _spanContextExtractor.Extract(
httpHeadersToken.ToObject<string[][]>(),
getter: GetHeaderValues!);

// This implementation is .NET v3 specific, and assumes that the span returned by StartActive is a DuckType
var extractedContext = manualExtractedContext?.GetType()
.GetProperty("Instance", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
?.GetValue(manualExtractedContext);
var parentTraceId = ActivityTraceId.CreateFromString(RawTraceId.GetValue(extractedContext) as string);
var parentSpanId = ActivitySpanId.CreateFromString(RawSpanId.GetValue(extractedContext) as string);
var flags = (SamplingPriority.GetValue(extractedContext) as int?) > 0 ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None;
var datadogHeadersTracestate = W3CTraceContextCreateTraceStateHeader.Invoke(null, new object[] { extractedContext! });
var tracestate = (string?)httpHeadersToken[1][0] == "tracestate" ? (string?)httpHeadersToken[1][1] : datadogHeadersTracestate;

contextToLink = new ActivityContext(
parentTraceId,
parentSpanId,
flags,
(string?)tracestate,
isRemote: true);
}

ActivityContext contextToLink = FindActivity(parentSpanLink).Context;
linksList.Add(new ActivityLink(contextToLink, tags));
}
}
Expand Down
3 changes: 0 additions & 3 deletions utils/build/docker/golang/parametric/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ type StartSpanArgs struct {
Resource string `json:"resource,omitempty"`
Type string `json:"type,omitempty"`
Origin string `json:"origin,omitempty"`
HttpHeaders []Tuple `json:"http_headers,omitempty"`
SpanTags []Tuple `json:"span_tags,omitempty"`
SpanLinks []SpanLink `json:"span_links,omitempty"`
}
Expand All @@ -28,7 +27,6 @@ type Tuple []string

type SpanLink struct {
ParentId uint64 `json:"parent_id"`
HttpHeaders []Tuple `json:"http_headers"`
Attributes AttributeKeyVals `json:"attributes,omitempty"`
}

Expand Down Expand Up @@ -98,7 +96,6 @@ type OtelStartSpanArgs struct {
Type string `json:"type"`
Timestamp int64 `json:"timestamp"`
SpanLinks []SpanLink `json:"links"`
HttpHeaders []Tuple `json:"http_headers"`
Attributes AttributeKeyVals `json:"attributes"`
}

Expand Down
Loading

0 comments on commit d1e67f6

Please sign in to comment.