Skip to content
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8f7f60f
Allow Config to resolve traceURL; pass this as a field into httptrans…
mtoffl01 Mar 23, 2026
3e3fec8
Fix otlp protocol resolution and add tests for all new logic
mtoffl01 Mar 23, 2026
c559f8f
Implement OTEL_EXPORTER_OTLP_TRACES_HEADERS
mtoffl01 Mar 23, 2026
cb6908e
Fix superfluous referecne to unsupport OTLP header config
mtoffl01 Mar 23, 2026
a85fedf
Introduce otlpExportMode on Config; use this instead of traceProtocol…
mtoffl01 Mar 23, 2026
33a9a37
fix relationship between otlpexportmode and protocol
mtoffl01 Mar 23, 2026
3e51fdd
replace tracer.config.otlpExportMode with internalconfig otlpExportMode
mtoffl01 Mar 23, 2026
5e252be
Clean up resolveOTLPHeaders
mtoffl01 Mar 23, 2026
51f72a2
cleanup
mtoffl01 Mar 23, 2026
b71eec7
more cleanup and remove OTEL_EXPORTER_OTLP_ENDPOINT
mtoffl01 Mar 23, 2026
f48d44f
Store otlpTraceURL instead of traceURL on Config; let tracer resolve …
mtoffl01 Mar 23, 2026
f4360b4
Make DD_TRACE_AGENT_PROTOCOL_VERSION override OTEL_TRACES_EXPORTER
mtoffl01 Mar 23, 2026
49d0937
Merge branch 'main' into mtoff/otlp-export-config
kakkoyun Mar 24, 2026
a88b7b2
Use GetMap to resolve OTEL_EXPORTER_OTLP_TRACES_HEADERS and change co…
mtoffl01 Mar 24, 2026
d475920
give OTEL_EXPORTER_OTLP_TRACES_ENDPOINT default value of null
mtoffl01 Mar 24, 2026
8188bbe
Merge branch 'main' into mtoff/otlp-export-config
kakkoyun Mar 25, 2026
af909c3
flesh out traceWriterOtlp
mtoffl01 Mar 24, 2026
9246084
add new otlpSender for transporting otlp traces specifically; build o…
mtoffl01 Mar 24, 2026
a934fdd
rename file payload_otlp_convert.go -> span_to_otlp.go; and introduce…
mtoffl01 Mar 24, 2026
2cfa217
introduce otlp_writer_test.go: basic otlp writer tests
mtoffl01 Mar 24, 2026
7bba29d
Fix: only export sampled spans, and correct telemetry.sdk.version value
mtoffl01 Mar 24, 2026
89cb7e6
Test buildResource
mtoffl01 Mar 24, 2026
da1efd4
implement edge case: add 'service.name' attribute to span when span's…
mtoffl01 Mar 24, 2026
49053c1
add comment about s.metaStruct
mtoffl01 Mar 25, 2026
378639f
fix(ddtrace/tracer): pre-compute `_dd.p.dm` numeric value at write-ti…
darccio Mar 25, 2026
97a2bf9
fix(ddtrace/tracer): check `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABL…
darccio Mar 25, 2026
ffbea4e
chore: disable automated dependency updater config [incident-51602]
moezein0 Mar 25, 2026
9882338
merge conflicts
mtoffl01 Mar 25, 2026
5072b2a
merge conflicts
mtoffl01 Mar 25, 2026
78e160f
Apply suggestion from @genesor
mtoffl01 Mar 25, 2026
f0c6f00
address PR review comments
mtoffl01 Mar 25, 2026
5ca4d0e
Merge branch 'mtoff/otlp-export-config' into mtoff/otlp-export-traces
mtoffl01 Mar 25, 2026
6deec12
Merge branch 'main' into mtoff/otlp-export-traces
mtoffl01 Mar 25, 2026
996c4d7
rename transport to ddtransport: be clear
mtoffl01 Mar 25, 2026
697c2ba
remove otlpSender; make otlpTraceWriter handle its own http client
mtoffl01 Mar 25, 2026
d7749bb
introduce new otlpTransport, plus tests
mtoffl01 Mar 25, 2026
dcd8a10
cleanup setting transport on tracer.config in non-otlp conditions
mtoffl01 Mar 25, 2026
6dbab08
fix imports
mtoffl01 Mar 25, 2026
d0765c8
fix lint errors
mtoffl01 Mar 25, 2026
acc9688
Add comment reminder about benchmarking protobuf
mtoffl01 Mar 25, 2026
490d6f1
implement flush condition if hit payloadMaxSize
mtoffl01 Mar 25, 2026
c07bf3c
Write benchmarks for otlp trace writer
mtoffl01 Mar 25, 2026
90dccdd
fix nits from CI
mtoffl01 Mar 26, 2026
99ef3c8
Add last span atttributes and attribute limit, as well as tracestate …
mtoffl01 Mar 26, 2026
47a01a2
disable stats concentrator in otlp mode
mtoffl01 Mar 26, 2026
ddfc0e2
fix checklocks on buffer
mtoffl01 Mar 26, 2026
c371138
fix gofmt otlp_writer.go
mtoffl01 Mar 26, 2026
ac556e6
Merge branch 'main' into mtoff/otlp-export-traces
mtoffl01 Mar 27, 2026
0e4f52b
Merge branch 'mtoff/otlp-export-traces' into mtoff/stats-disabled-otlp
mtoffl01 Mar 27, 2026
cd63872
Fix converSpanAttribute
mtoffl01 Mar 27, 2026
12d75b3
Apply suggestions from code review
mtoffl01 Mar 27, 2026
af69c6b
Make sure newBenchOTLPWriter has scope
mtoffl01 Mar 27, 2026
35cf849
Address agent UDS url issue: have otlp writer create its own client
mtoffl01 Mar 30, 2026
6480f53
PR comments: promote service.name and operation.name attributes; omit…
mtoffl01 Apr 3, 2026
20c74c4
Merge branch 'mtoff/otlp-export-traces' into mtoff/stats-disabled-otlp
mtoffl01 Apr 6, 2026
e8aab3a
Add new benchmarks to BENCHMARK_TARGETS
mtoffl01 Apr 7, 2026
4223bd3
Merge branch 'main' into mtoff/otlp-export-traces
mtoffl01 Apr 7, 2026
037eec3
Fix: Capture response body in otlpTransport.send, in order to reuse t…
mtoffl01 Apr 7, 2026
ca17d6a
fix lint problem in otlp_transport_test
mtoffl01 Apr 7, 2026
63bc9ea
Fix race condition in TestOTLPTransportConnectionReuse
mtoffl01 Apr 7, 2026
f9ae432
Merge branch 'main' into mtoff/otlp-export-traces
kakkoyun Apr 8, 2026
91fac2a
drain otlpTransport response body after reading status code
mtoffl01 Apr 9, 2026
7c12988
Cache base TracesData size
mtoffl01 Apr 9, 2026
f577ca2
Use Span_SPAN_KIND_UNSPECIFIED for unknown span types
mtoffl01 Apr 9, 2026
ceea3dd
wrap error in otlpTransport.send
mtoffl01 Apr 9, 2026
b3a521d
enforce Content-Type header at the otlpTransport.send level
mtoffl01 Apr 9, 2026
9b8e37f
Merge branch 'mtoff/otlp-export-traces' into mtoff/stats-disabled-otlp
mtoffl01 Apr 10, 2026
8d6156e
Merge branch 'main' into mtoff/otlp-export-traces
kakkoyun Apr 14, 2026
6ec6405
Address codex feedback on span_to_otlp
mtoffl01 Apr 14, 2026
df091b3
Fix nil check on otlpArrayValue converter
mtoffl01 Apr 14, 2026
92de62c
Document maxAttributesCount
mtoffl01 Apr 14, 2026
016cb71
Restore default traceWriter test
mtoffl01 Apr 14, 2026
b19d2aa
Merge branch 'mtoff/otlp-export-traces' into mtoff/stats-disabled-otlp
mtoffl01 Apr 14, 2026
3bb0a70
Use noopConcentrator instead of nil checks
mtoffl01 Apr 14, 2026
87989c6
modernize for loop to range
mtoffl01 Apr 14, 2026
75e2a69
merge conflicts
mtoffl01 Apr 15, 2026
71deeda
Change stats logic to do newConcentrator by default with a conditiona…
mtoffl01 Apr 15, 2026
af089cc
Merge branch 'main' into mtoff/stats-disabled-otlp
kakkoyun Apr 17, 2026
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
4 changes: 2 additions & 2 deletions ddtrace/tracer/civisibility_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const (
EvpProxyPath = "evp_proxy/v2" // Path for EVP proxy.
)

// Ensure that civisibilityTransport implements the transport interface.
var _ transport = (*ciVisibilityTransport)(nil)
// Ensure that ciVisibilityTransport implements the ddTransport interface.
var _ ddTransport = (*ciVisibilityTransport)(nil)

// ciVisibilityTransport is a structure that handles sending CI Visibility payloads
// to the Datadog endpoint, either in agentless mode or through the EVP proxy.
Expand Down
4 changes: 2 additions & 2 deletions ddtrace/tracer/civisibility_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (w *ciVisibilityTraceWriter) flush() {
var err error

requestCompressedType := telemetry.UncompressedRequestCompressedType
if ciTransport, ok := w.config.transport.(*ciVisibilityTransport); ok && ciTransport.agentless {
if ciTransport, ok := w.config.ddTransport.(*ciVisibilityTransport); ok && ciTransport.agentless {
requestCompressedType = telemetry.CompressedRequestCompressedType
}
telemetry.EndpointPayloadRequests(telemetry.TestCycleEndpointType, requestCompressedType)
Expand All @@ -117,7 +117,7 @@ func (w *ciVisibilityTraceWriter) flush() {
stats := p.stats()
size, count = stats.size, stats.itemCount
log.Debug("ciVisibilityTraceWriter: sending payload: size: %d events: %d\n", size, count)
_, err = w.config.transport.send(p.payload)
_, err = w.config.ddTransport.send(p.payload)
if err == nil {
log.Debug("ciVisibilityTraceWriter: sent events after %d attempts", attempt+1)
return
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/civisibility_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestCiVisibilityTraceWriterFlushRetries(t *testing.T) {
assert: assert,
}
c, err := newTestConfig(func(c *config) {
c.transport = p
c.ddTransport = p
c.sendRetries = test.configRetries
c.internalConfig.SetRetryInterval(test.retryInterval, internalconfig.OriginCode)
})
Expand Down
4 changes: 2 additions & 2 deletions ddtrace/tracer/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func logStartup(t *tracer) {
if srcURL := t.config.internalConfig.RawAgentURL(); srcURL != nil && srcURL.Scheme == "unix" {
agentURL = srcURL.String()
} else {
agentURL = t.config.transport.endpoint()
agentURL = t.config.ddTransport.endpoint()
}
info := startupInfo{
Date: time.Now().Format(time.RFC3339),
Expand Down Expand Up @@ -170,7 +170,7 @@ func logStartup(t *tracer) {
info.SampleRateLimit = fmt.Sprintf("%v", limit)
}
if !t.config.internalConfig.LogToStdout() {
if err := checkEndpoint(t.config.httpClient, t.config.transport.endpoint(), t.config.internalConfig.TraceProtocol()); err != nil {
if err := checkEndpoint(t.config.httpClient, t.config.ddTransport.endpoint(), t.config.internalConfig.TraceProtocol()); err != nil {
info.AgentError = err.Error()
log.Warn("DIAGNOSTICS Unable to reach agent intake: %s", err.Error())
}
Expand Down
23 changes: 11 additions & 12 deletions ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ type config struct {
// all spans.
globalTags dynamicConfig[map[string]any]

// transport specifies the Transport interface which will be used to send data to the agent.
transport transport
// ddTransport specifies the Datadog transport used to send msgpack traces and stats to the agent.
ddTransport ddTransport

// httpClientTimeout specifies the timeout for the HTTP client.
httpClientTimeout time.Duration
Expand Down Expand Up @@ -417,10 +417,10 @@ func newConfig(opts ...StartOption) (*config, error) {
} else {
globalconfig.SetServiceName(c.internalConfig.ServiceName())
}
if c.transport == nil {
if c.ddTransport == nil {
agentURL := c.internalConfig.AgentURL().String()
traceURL, headers := resolveTraceTransport(c.internalConfig)
c.transport = newHTTPTransport(traceURL, agentURL+statsAPIPath, c.httpClient, headers)
c.ddTransport = newHTTPTransport(traceURL, agentURL+statsAPIPath, c.httpClient, headers)
}
if c.propagator == nil {
envKey := "DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH"
Expand Down Expand Up @@ -448,7 +448,7 @@ func newConfig(opts ...StartOption) (*config, error) {
c.httpClientTimeout = time.Second * 45 // Increase timeout up to 45 seconds (same as other tracers in CIVis mode)
c.internalConfig.SetLogStartup(false, internalconfig.OriginCalculated) // If we are in CI Visibility mode we don't want to log the startup to stdout to avoid polluting the output
ciTransport := newCiVisibilityTransport(c) // Create a default CI Visibility Transport
c.transport = ciTransport // Replace the default transport with the CI Visibility transport
c.ddTransport = ciTransport // Replace the default transport with the CI Visibility transport
c.ciVisibilityAgentless = ciTransport.agentless
c.ciVisibilityNoopTracer = internal.BoolEnv(constants.CIVisibilityUseNoopTracer, false)
}
Expand All @@ -461,7 +461,7 @@ func newConfig(opts ...StartOption) (*config, error) {
// If the agent doesn't support the v1 protocol, downgrade to v0.4
if c.internalConfig.TraceProtocol() == traceProtocolV1 && !af.v1ProtocolAvailable {
c.internalConfig.SetTraceProtocol(traceProtocolV04, internalconfig.OriginCalculated)
if t, ok := c.transport.(*httpTransport); ok {
if t, ok := c.ddTransport.(*httpTransport); ok {
t.traceURL = agentURL.String() + tracesAPIPath
}
}
Expand Down Expand Up @@ -529,12 +529,11 @@ func apmTracingDisabled(c *config) {
c.internalConfig.SetRuntimeMetricsV2Enabled(false, internalconfig.OriginCalculated)
}

// resolveTraceTransport returns the trace URL and headers for the transport
// based on whether OTLP export mode is active.
// resolveTraceTransport returns the trace URL and headers for the Datadog
// agent transport. In OTLP export mode the ddTransport is not used for trace
// sending (otlpTransport handles that), but it may still be used for stats
// and agent discovery, so it always points at the DD agent.
func resolveTraceTransport(cfg *internalconfig.Config) (traceURL string, headers map[string]string) {
if cfg.OTLPExportMode() {
return cfg.OTLPTraceURL(), cfg.OTLPHeaders()
}
agentURL := cfg.AgentURL().String()
traceURL = agentURL + tracesAPIPath
if cfg.TraceProtocol() == traceProtocolV1 {
Expand Down Expand Up @@ -1455,7 +1454,7 @@ func WithTestDefaults(statsdClient any) StartOption {
statsdClient = &statsd.NoOpClientDirect{}
}
c.statsdClient = statsdClient.(internal.StatsdClient)
c.transport = newDummyTransport()
c.ddTransport = newDummyTransport()
}
}

Expand Down
4 changes: 2 additions & 2 deletions ddtrace/tracer/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ import (
"github.com/DataDog/dd-trace-go/v2/internal/version"
)

func withTransport(t transport) StartOption {
func withTransport(t ddTransport) StartOption {
return func(c *config) {
c.transport = t
c.ddTransport = t
}
}

Expand Down
48 changes: 48 additions & 0 deletions ddtrace/tracer/otlp_transport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.

package tracer

import (
"bytes"
"fmt"
"net/http"
)

// otlpTransport sends protobuf-encoded OTLP payloads over HTTP.
// It is the OTLP counterpart to httpTransport (which handles Datadog-protocol traffic).
type otlpTransport struct {
client *http.Client
endpoint string
headers map[string]string
}

func newOTLPTransport(client *http.Client, endpoint string, headers map[string]string) *otlpTransport {
return &otlpTransport{
client: client,
endpoint: endpoint,
headers: headers,
}
}

// send posts a protobuf-encoded payload to the configured OTLP endpoint.
func (t *otlpTransport) send(data []byte) error {
req, err := http.NewRequest("POST", t.endpoint, bytes.NewReader(data))
if err != nil {
return fmt.Errorf("cannot create http request: %s", err)
}
for header, value := range t.headers {
req.Header.Set(header, value)
}
resp, err := t.client.Do(req)
if err != nil {
return err
}
resp.Body.Close()
if code := resp.StatusCode; code >= 400 {
return fmt.Errorf("HTTP %d: %s", code, http.StatusText(code))
}
return nil
}
97 changes: 97 additions & 0 deletions ddtrace/tracer/otlp_transport_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.

package tracer

import (
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestOTLPTransportSendSuccess(t *testing.T) {
var received []byte
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
received, _ = io.ReadAll(r.Body)
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

tr := newOTLPTransport(srv.Client(), srv.URL, map[string]string{"Content-Type": "application/x-protobuf"})
err := tr.send([]byte("hello"))
require.NoError(t, err)
assert.Equal(t, []byte("hello"), received)
}

func TestOTLPTransportSendHeaders(t *testing.T) {
var gotHeaders http.Header
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotHeaders = r.Header.Clone()
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

headers := map[string]string{
"Content-Type": "application/x-protobuf",
"Api-Key": "secret",
"X-Custom": "value",
}
tr := newOTLPTransport(srv.Client(), srv.URL, headers)
err := tr.send([]byte("data"))
require.NoError(t, err)

assert.Equal(t, "application/x-protobuf", gotHeaders.Get("Content-Type"))
assert.Equal(t, "secret", gotHeaders.Get("Api-Key"))
assert.Equal(t, "value", gotHeaders.Get("X-Custom"))
}

func TestOTLPTransportSendHTTPMethod(t *testing.T) {
var gotMethod string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotMethod = r.Method
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

tr := newOTLPTransport(srv.Client(), srv.URL, nil)
err := tr.send([]byte("data"))
require.NoError(t, err)
assert.Equal(t, "POST", gotMethod)
}

func TestOTLPTransportSendErrorStatus(t *testing.T) {
tests := []struct {
code int
text string
}{
{http.StatusBadRequest, "Bad Request"},
{http.StatusUnauthorized, "Unauthorized"},
{http.StatusInternalServerError, "Internal Server Error"},
{http.StatusServiceUnavailable, "Service Unavailable"},
}
for _, tt := range tests {
t.Run(tt.text, func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.code)
}))
defer srv.Close()

tr := newOTLPTransport(srv.Client(), srv.URL, nil)
err := tr.send([]byte("data"))
require.Error(t, err)
assert.Contains(t, err.Error(), tt.text)
})
}
}

func TestOTLPTransportSendConnectionError(t *testing.T) {
tr := newOTLPTransport(http.DefaultClient, "http://127.0.0.1:0/nonexistent", nil)
err := tr.send([]byte("data"))
require.Error(t, err)
}
Loading
Loading