From 6356aee4c69b51f27e7e82e43b1797f60b74905a Mon Sep 17 00:00:00 2001 From: Bryan English Date: Thu, 16 Jul 2026 11:27:32 -0400 Subject: [PATCH] fix(trace-exporter): mark OTLP client-computed stats When tracers compute span metrics outside libdatadog, OTLP trace exports still need to mark the resource and direct Agent request so the Agent does not compute the same stats again. Use the tracer metadata flag in addition to libdatadog's native OTLP stats worker state. --- libdd-data-pipeline/src/trace_exporter/mod.rs | 5 +++-- .../tests/test_trace_exporter_otlp_export.rs | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libdd-data-pipeline/src/trace_exporter/mod.rs b/libdd-data-pipeline/src/trace_exporter/mod.rs index 90f6bd1b08..58dbb020fc 100644 --- a/libdd-data-pipeline/src/trace_exporter/mod.rs +++ b/libdd-data-pipeline/src/trace_exporter/mod.rs @@ -676,7 +676,8 @@ impl< r.language = self.metadata.language.clone(); r.tracer_version = self.metadata.tracer_version.clone(); r.runtime_id = self.metadata.runtime_id.clone(); - r.client_computed_stats = self.otlp_stats_enabled; + r.client_computed_stats = + self.metadata.client_computed_stats || self.otlp_stats_enabled; r.instrumentation_scope_name = config.instrumentation_scope_name.clone(); r.instrumentation_scope_version = config.instrumentation_scope_version.clone(); r @@ -694,7 +695,7 @@ impl< })?; // Also set the header: resource attributes survive Collector hops, headers don't. let effective_config; - let config_to_use = if self.otlp_stats_enabled { + let config_to_use = if self.metadata.client_computed_stats || self.otlp_stats_enabled { effective_config = { let mut c = config.clone(); c.headers.insert( diff --git a/libdd-data-pipeline/tests/test_trace_exporter_otlp_export.rs b/libdd-data-pipeline/tests/test_trace_exporter_otlp_export.rs index 42873f64bf..290dcb8409 100644 --- a/libdd-data-pipeline/tests/test_trace_exporter_otlp_export.rs +++ b/libdd-data-pipeline/tests/test_trace_exporter_otlp_export.rs @@ -28,19 +28,26 @@ mod otlp_export_tests { let server = MockServer::start_async().await; // Assert the OTLP request structure using json_body_includes matchers. - // resourceSpans must be present with the correct service.name and environment resource - // attributes, and spans must contain the expected name prefix. + // resourceSpans must be present with the expected resource attributes, including + // client-computed-stats, and the redundant header must be set for direct Agent requests. let mut mock = server .mock_async(|when, then| { when.method("POST") .path("/v1/traces") .header("content-type", "application/json") + .header("datadog-client-computed-stats", "yes") .json_body_includes( serde_json::json!({ "resourceSpans": [{ "resource": { "attributes": [ {"key": "service.name", "value": {"stringValue": "test"}}, + {"key": "deployment.environment.name", "value": {"stringValue": "test_env"}}, + {"key": "telemetry.sdk.name", "value": {"stringValue": "datadog"}}, + {"key": "telemetry.sdk.language", "value": {"stringValue": "test-lang"}}, + {"key": "telemetry.sdk.version", "value": {"stringValue": "1.0"}}, + {"key": "runtime-id", "value": {"stringValue": "test-runtime-id"}}, + {"key": "_dd.stats_computed", "value": {"stringValue": "true"}}, ] } }] @@ -63,7 +70,9 @@ mod otlp_export_tests { .set_language_interpreter("interpreter") .set_tracer_version("1.0") .set_env("test_env") - .set_service("test"); + .set_service("test") + .set_runtime_id("test-runtime-id") + .set_client_computed_stats(); let trace_exporter = builder .build::()