Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tracing] Add test and endpoints to test OTEL Drop-In Support of the OpenTelemetry Propagators API #3782

Merged
merged 22 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
27797f7
Add new test case for applications using the default OTEL propagator …
zacharycmontoya Jan 8, 2025
21c9bb5
Add endpoint in .NET weblog app with the workaround that needs to be …
zacharycmontoya Jan 8, 2025
d717322
Revert fixup made in the .NET weblog app and rely on the tracer to im…
zacharycmontoya Jan 8, 2025
20c372b
Add missing_feature label for .NET
zacharycmontoya Jan 8, 2025
6ee5aac
Add test skip for cpp
zacharycmontoya Jan 8, 2025
5841fa3
WIP
zacharycmontoya Jan 10, 2025
57cd594
Move the Test_Otel_Drop_In_Default_Propagator class to the APM_TRACIN…
zacharycmontoya Jan 14, 2025
5a26464
Update test to remove "headers" subkey
zacharycmontoya Jan 17, 2025
8fc2cc3
Run tests against Java
zacharycmontoya Jan 17, 2025
02ff8d5
Run tests against Python
zacharycmontoya Jan 17, 2025
a6e0643
Begin to implement the Node.js test endpoint
zacharycmontoya Jan 17, 2025
ca1e4e8
Run tests against Go. This one differs specifically because we don't …
zacharycmontoya Jan 18, 2025
5704c0d
Run tests against Ruby. The default extraction is failing, but the de…
zacharycmontoya Jan 24, 2025
0498f59
Add to the PHP manifest with an "incomplete_test_app" skip
zacharycmontoya Jan 24, 2025
8e4f6b1
Fix lint errors
zacharycmontoya Jan 24, 2025
9a63b1b
Fix lint issues in Node.js express weblog app
zacharycmontoya Jan 27, 2025
c5f4f8b
Add test skip for Node.js because the extract endpoint now seems to fail
zacharycmontoya Jan 27, 2025
8f93018
Move the new test into its own file
zacharycmontoya Jan 28, 2025
82fd416
Rename test case to align with the test file name
zacharycmontoya Jan 28, 2025
770adf8
Remove unnecessary changes from test_otel_drop_in.py
zacharycmontoya Jan 28, 2025
8179952
Update the test cases to use the new OpenTelemetry Propagators API fe…
zacharycmontoya Jan 28, 2025
ebd56ac
Update weblog test skips with "incomplete_test_app (endpoint not impl…
zacharycmontoya Jan 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion manifests/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ tests/:
test_inferred_proxy.py:
Test_AWS_API_Gateway_Inferred_Span_Creation: missing_feature
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
Test_Otel_Drop_In: irrelevant (library does not implement OpenTelemetry)
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api: irrelevant (library does not implement OpenTelemetry)
parametric/:
test_128_bit_traceids.py:
Test_128_Bit_Traceids: missing_feature (parametric app does not support trace/span/extract endpoint)
Expand Down
3 changes: 3 additions & 0 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ tests/:
Test_AWS_API_Gateway_Inferred_Span_Creation: missing_feature
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api: v3.9.0
parametric/:
test_config_consistency.py:
Test_Config_Dogstatsd: missing_feature (does not support hostname)
Expand Down
5 changes: 5 additions & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@ tests/:
Test_AWS_API_Gateway_Inferred_Span_Creation: missing_feature
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api:
'*': incomplete_test_app (endpoint not implemented)
net-http: v1.70.1
parametric/:
test_config_consistency.py:
Test_Config_Dogstatsd: v1.72.0-dev
Expand Down
5 changes: 5 additions & 0 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,11 @@ tests/:
spring-boot: v1.39.0
test_sql.py:
Test_Sql: bug (APMAPI-729)
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api:
'*': incomplete_test_app (endpoint not implemented)
spring-boot: v1.39.0
parametric/:
test_config_consistency.py:
Test_Config_Dogstatsd: missing_feature (default hostname is inconsistent)
Expand Down
6 changes: 6 additions & 0 deletions manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,12 @@ tests/:
express5: *ref_5_26_0
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api:
'*': incomplete_test_app (endpoint not implemented)
express4: *ref_5_26_0
express5: *ref_5_26_0
parametric/:
test_128_bit_traceids.py:
Test_128_Bit_Traceids: *ref_3_0_0
Expand Down
3 changes: 3 additions & 0 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ tests/:
Test_AWS_API_Gateway_Inferred_Span_Creation: missing_feature
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api: incomplete_test_app (endpoint not implemented)
parametric/:
test_128_bit_traceids.py:
Test_128_Bit_Traceids: v0.84.0
Expand Down
5 changes: 5 additions & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,11 @@ tests/:
Test_AWS_API_Gateway_Inferred_Span_Creation: missing_feature
test_otel_drop_in.py:
Test_Otel_Drop_In: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api:
'*': incomplete_test_app (endpoint not implemented)
flask-poc: v2.19.0
parametric/:
test_128_bit_traceids.py:
Test_128_Bit_Traceids: v2.6.0
Expand Down
5 changes: 5 additions & 0 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ tests/:
TestK8sLibInjectioProfilingClusterEnabled: missing_feature
TestK8sLibInjectioProfilingClusterOverride: missing_feature
TestK8sLibInjectioProfilingDisabledByDefault: missing_feature
otel/:
test_context_propagation.py:
Test_Otel_Context_Propagation_Default_Propagator_Api:
'*': incomplete_test_app (endpoint not implemented)
rails70: v2.0.0
parametric/:
test_config_consistency.py:
Test_Config_Dogstatsd: missing_feature
Expand Down
41 changes: 41 additions & 0 deletions tests/otel/test_context_propagation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2024 Datadog, Inc.

import json
from utils import weblog, interfaces, scenarios, features, incomplete_test_app


@features.otel_propagators_api
@scenarios.apm_tracing_e2e_otel
class Test_Otel_Context_Propagation_Default_Propagator_Api:
def setup_propagation_extract(self):
extract_headers = {
"traceparent": "00-11111111111111110000000000000002-000000000000000a-01",
"tracestate": "dd=s:2;p:000000000000000a,foo=1",
"baggage": "foo=1",
}
self.r = weblog.get("/otel_drop_in_default_propagator_extract", headers=extract_headers)

@incomplete_test_app(library="nodejs", reason="Node.js extract endpoint doesn't seem to be working.")
@incomplete_test_app(library="ruby", reason="Ruby extract seems to fail even though it should be supported")
def test_propagation_extract(self):
content = json.loads(self.r.text)

assert content["trace_id"] == 2
assert content["span_id"] == 10
assert content["tracestate"] and not content["tracestate"].isspace()
# assert content["baggage"] and not content["baggage"].isspace()

def setup_propagation_inject(self):
inject_headers = {
"baggage": "foo=2",
}
self.r = weblog.get("/otel_drop_in_default_propagator_inject")

@incomplete_test_app(library="nodejs", reason="Node.js inject endpoint doesn't seem to be working.")
def test_propagation_inject(self):
content = json.loads(self.r.text)

assert content["traceparent"] and not content["traceparent"].isspace()
# assert content["baggage"] and not content["baggage"].isspace()
9 changes: 9 additions & 0 deletions utils/_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -2374,5 +2374,14 @@ def debugger_code_origins(test_object):
pytest.mark.features(feature_id=360)(test_object)
return test_object

@staticmethod
def otel_propagators_api(test_object):
"""OpenTelemetry Propagators API

https://feature-parity.us1.prod.dog/#/?feature=361
"""
pytest.mark.features(feature_id=361)(test_object)
return test_object


features = _Features()
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;

namespace weblog
{
public class OtelDropInEndpoint : ISystemTestEndpoint
{
public void Register(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routeBuilder)
{
routeBuilder.MapGet("/otel_drop_in_default_propagator_extract", async context =>
{
var parentContext = OpenTelemetryInstrumentation.Propagator.Extract(default, context.Request.Headers, (carrier, key) =>
{
return carrier.TryGetValue(key, out var value) && value.Count >= 1 ? new[] { value[0] } : null;
});

var ddTraceId = Convert.ToUInt64(parentContext.ActivityContext.TraceId.ToHexString().Substring(16), 16);
var ddSpanId = Convert.ToUInt64(parentContext.ActivityContext.SpanId.ToHexString(), 16);

var data = new
{
trace_id = ddTraceId,
span_id = ddSpanId,
tracestate = parentContext.ActivityContext.TraceState,
baggage = parentContext.Baggage
};

await context.Response.WriteAsync(JsonSerializer.Serialize(data));
});

routeBuilder.MapGet("/otel_drop_in_default_propagator_inject", async context =>
{
var headersDict = new Dictionary<string,string>();
OpenTelemetryInstrumentation.Propagator.Inject(new PropagationContext(Activity.Current.Context, Baggage.Current), headersDict, (carrier, key, value) =>
{
carrier[key] = value;
});

await context.Response.WriteAsync(JsonSerializer.Serialize(headersDict));
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Diagnostics;
using OpenTelemetry.Context.Propagation;

namespace weblog
{
public static class OpenTelemetryInstrumentation
{
public static TextMapPropagator Propagator { get; } = Propagators.DefaultTextMapPropagator;
}
}
1 change: 1 addition & 0 deletions utils/build/docker/dotnet/weblog/app.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@
<PackageReference Include="MongoDB.Driver" Version="2.23.1"/>

<PackageReference Include="Datadog.Trace" Version="*" />
<PackageReference Include="OpenTelemetry.Api" Version="1.10.0" />
</ItemGroup>
</Project>
112 changes: 112 additions & 0 deletions utils/build/docker/golang/app/net-http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
oteltrace "go.opentelemetry.io/otel/trace"
otelbaggage "go.opentelemetry.io/otel/baggage"

"gopkg.in/DataDog/dd-trace-go.v1/appsec"
httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
Expand Down Expand Up @@ -537,6 +538,79 @@ func main() {
w.Write([]byte("ok"))
})

mux.HandleFunc("/otel_drop_in_default_propagator_extract", func(w http.ResponseWriter, r *http.Request) {
// Differing from other languages, the user must set the text map propagator because dd-trace-go
// doesn't automatically instrument at runtime (not including Orchestrion)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

httpCarrier := HttpCarrier{header: r.Header}

propagator := otel.GetTextMapPropagator()
ctx := propagator.Extract(r.Context(), httpCarrier)

spanContext := oteltrace.SpanContextFromContext(ctx)
baggage := otelbaggage.FromContext(ctx)

base := 16
bitSize := 64
result := make(map[string]any, 4)

num, err := strconv.ParseInt(spanContext.TraceID().String()[16:], base, bitSize)
if err == nil {
result["trace_id"] = num
}

num, err = strconv.ParseInt(spanContext.SpanID().String(), base, bitSize)
if err == nil {
result["span_id"] = num
}

result["tracestate"] = spanContext.TraceState().String()
result["baggage"] = baggage.String()

jsonData, err := json.Marshal(result)
if err != nil {
w.WriteHeader(422)
w.Write([]byte("failed to convert carrier to JSON"))
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
w.Write(jsonData)
})

mux.HandleFunc("/otel_drop_in_default_propagator_inject", func(w http.ResponseWriter, r *http.Request) {
// Differing from other languages, the user must set the text map propagator because dd-trace-go
// doesn't automatically instrument at runtime (not including Orchestrion)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))

ctx := context.Background()
p := ddotel.NewTracerProvider()
tracer := p.Tracer("")
otel.SetTracerProvider(p)

_, span := tracer.Start(ddotel.ContextWithStartOptions(ctx), "main")
newCtx := oteltrace.ContextWithSpan(ctx, span)

propagator := otel.GetTextMapPropagator()
mapCarrier := make(MapCarrier)
propagator.Inject(newCtx, mapCarrier)

jsonData, err := json.Marshal(mapCarrier)
span.End()

if err != nil {
w.WriteHeader(422)
w.Write([]byte("failed to convert carrier to JSON"))
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
w.Write(jsonData)
})

mux.HandleFunc("/session/new", func(w http.ResponseWriter, r *http.Request) {
sessionID := strconv.Itoa(rand.Int())
w.Header().Add("Set-Cookie", "session="+sessionID+"; Path=/; Max-Age=3600; Secure; HttpOnly")
Expand Down Expand Up @@ -598,6 +672,44 @@ func (c carrier) ForeachKey(handler func(key, val string) error) error {
return nil
}

type MapCarrier map[string]string

func (c MapCarrier) Get(key string) string {
return c[key]
}

func (c MapCarrier) Set(key, val string) {
c[key] = val
}

func (c MapCarrier) Keys() []string {
keys := make([]string, 0, len(c))
for k := range c {
keys = append(keys, k)
}
return keys
}

type HttpCarrier struct {
header http.Header
}

func (c HttpCarrier) Get(key string) string {
return c.header.Get(key)
}

func (c HttpCarrier) Set(key, val string) {
c.header.Set(key, val)
}

func (c HttpCarrier) Keys() []string {
keys := make([]string, 0, len(c.header))
for k := range c.header {
keys = append(keys, k)
}
return keys
}

func write(w http.ResponseWriter, r *http.Request, d []byte) {
span, _ := ddtracer.StartSpanFromContext(r.Context(), "child.span")
defer span.Finish()
Expand Down
Loading