From d0d4f673d58505daffcf4123bf694642b078f967 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Fri, 27 Feb 2026 18:17:50 +0100 Subject: [PATCH 1/4] feat(tracer): publish tracer metadata to OTel This commit hooks up the new OTel process context publication into the existing tracer metadata publication, so that both are done automatically at once. --- .../src/tracer_metadata.rs | 2 + libdd-library-config/src/tracer_metadata.rs | 60 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/libdd-library-config-ffi/src/tracer_metadata.rs b/libdd-library-config-ffi/src/tracer_metadata.rs index 73f2005fe5..f4d3f6530f 100644 --- a/libdd-library-config-ffi/src/tracer_metadata.rs +++ b/libdd-library-config-ffi/src/tracer_metadata.rs @@ -103,6 +103,8 @@ pub unsafe extern "C" fn ddog_tracer_metadata_set( } /// Serializes the `TracerMetadata` into a platform-specific memory handle (e.g., memfd on Linux). +/// This function also attempts to publish the tracer metadata as an OTel process context +/// separately, but will ignore resulting errors. /// /// # Safety /// - `ptr` must be a valid, non-null pointer to a `TracerMetadata`. diff --git a/libdd-library-config/src/tracer_metadata.rs b/libdd-library-config/src/tracer_metadata.rs index 4588860998..9fd4d035b1 100644 --- a/libdd-library-config/src/tracer_metadata.rs +++ b/libdd-library-config/src/tracer_metadata.rs @@ -1,5 +1,6 @@ // Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 +use libdd_trace_protobuf::opentelemetry::proto as otel_proto; use std::default::Default; /// This struct MUST be backward compatible. @@ -51,6 +52,60 @@ impl Default for TracerMetadata { } } +impl TracerMetadata { + // The value of the telemetry.sdk.name field to put in the otel context resource. + const OTEL_SDK_NAME: &str = "libdatadog"; + + pub fn to_otel_process_ctx(&self) -> otel_proto::common::v1::ProcessContext { + use otel_proto::common::v1::{any_value, AnyValue, KeyValue}; + + fn key_value(key: &'static str, val: String) -> KeyValue { + KeyValue { + key: key.to_owned(), + value: Some(AnyValue { + value: Some(any_value::Value::StringValue(val)), + }), + key_ref: 0, + } + } + + let mut attributes = vec![ + key_value("telemetry.sdk.language", self.tracer_language.clone()), + key_value("telemetry.sdk.version", self.tracer_version.clone()), + key_value("telemetry.sdk.name", Self::OTEL_SDK_NAME.to_owned()), + key_value("host.name", self.hostname.clone()), + ]; + + let mut set_opt_attr = |key: &'static str, val: &Option| { + if let Some(val) = val { + attributes.push(key_value(key, val.clone())) + } + }; + + set_opt_attr("service.name", &self.service_name); + set_opt_attr("service.instance.id", &self.runtime_id); + set_opt_attr("service.version", &self.service_version); + set_opt_attr("deployment.environment.name", &self.service_env); + set_opt_attr("container.id", &self.container_id); + + let extra_attributes: Vec<_> = self + .process_tags + .as_ref() + .map(|tags| key_value("datadog.process_tags", tags.clone())) + .into_iter() + .collect(); + + otel_proto::common::v1::ProcessContext { + resource: Some(otel_proto::resource::v1::Resource { + attributes, + dropped_attributes_count: 0, + entity_refs: vec![], + }), + extra_attributes, + } + } +} + pub enum AnonymousFileHandle { #[cfg(target_os = "linux")] Linux(memfd::Memfd), @@ -65,10 +120,13 @@ mod linux { use rand::Rng; use std::io::Write; - /// Create a memfd file storing the tracer metadata. + /// Create a memfd file storing the tracer metadata. This function also attempts to publish the + /// tracer metadata as an OTel process context separately, but will ignore resulting errors. pub fn store_tracer_metadata( data: &super::TracerMetadata, ) -> anyhow::Result { + let _ = crate::otel_process_ctx::linux::publish(&data.to_otel_process_ctx()); + let uid: String = rand::thread_rng() .sample_iter(&Alphanumeric) .take(8) From e64062d433c74d07a6efbebcca8164212060f9ae Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Fri, 6 Mar 2026 18:33:04 +0100 Subject: [PATCH 2/4] chore: make sure all tracer fields are exported --- libdd-library-config/src/tracer_metadata.rs | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/libdd-library-config/src/tracer_metadata.rs b/libdd-library-config/src/tracer_metadata.rs index 9fd4d035b1..6091e584ab 100644 --- a/libdd-library-config/src/tracer_metadata.rs +++ b/libdd-library-config/src/tracer_metadata.rs @@ -59,6 +59,22 @@ impl TracerMetadata { pub fn to_otel_process_ctx(&self) -> otel_proto::common::v1::ProcessContext { use otel_proto::common::v1::{any_value, AnyValue, KeyValue}; + // Every field of `self` should gets propagated to the otel context. + // If you add a new field, please also add it here and as a key/value in the otel context. + let TracerMetadata { + // This one isn't propagated on purpose + schema_version: _, + runtime_id, + tracer_language, + tracer_version, + hostname, + service_name, + service_env, + service_version, + process_tags, + container_id, + } = self; + fn key_value(key: &'static str, val: String) -> KeyValue { KeyValue { key: key.to_owned(), @@ -70,10 +86,10 @@ impl TracerMetadata { } let mut attributes = vec![ - key_value("telemetry.sdk.language", self.tracer_language.clone()), - key_value("telemetry.sdk.version", self.tracer_version.clone()), + key_value("telemetry.sdk.language", tracer_language.clone()), + key_value("telemetry.sdk.version", tracer_version.clone()), key_value("telemetry.sdk.name", Self::OTEL_SDK_NAME.to_owned()), - key_value("host.name", self.hostname.clone()), + key_value("host.name", hostname.clone()), ]; let mut set_opt_attr = |key: &'static str, val: &Option| { @@ -82,14 +98,13 @@ impl TracerMetadata { } }; - set_opt_attr("service.name", &self.service_name); - set_opt_attr("service.instance.id", &self.runtime_id); - set_opt_attr("service.version", &self.service_version); - set_opt_attr("deployment.environment.name", &self.service_env); - set_opt_attr("container.id", &self.container_id); + set_opt_attr("service.name", service_name); + set_opt_attr("service.instance.id", runtime_id); + set_opt_attr("service.version", service_version); + set_opt_attr("deployment.environment.name", service_env); + set_opt_attr("container.id", container_id); - let extra_attributes: Vec<_> = self - .process_tags + let extra_attributes: Vec<_> = process_tags .as_ref() .map(|tags| key_value("datadog.process_tags", tags.clone())) .into_iter() From 841cb88facd6a28908bbf53f33b87f42c210cd12 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Fri, 6 Mar 2026 18:36:58 +0100 Subject: [PATCH 3/4] chore: set empty string for unset fields in otel ctx --- libdd-library-config/src/tracer_metadata.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libdd-library-config/src/tracer_metadata.rs b/libdd-library-config/src/tracer_metadata.rs index 6091e584ab..641e60626e 100644 --- a/libdd-library-config/src/tracer_metadata.rs +++ b/libdd-library-config/src/tracer_metadata.rs @@ -93,9 +93,7 @@ impl TracerMetadata { ]; let mut set_opt_attr = |key: &'static str, val: &Option| { - if let Some(val) = val { - attributes.push(key_value(key, val.clone())) - } + attributes.push(key_value(key, val.as_ref().cloned().unwrap_or_default())) }; set_opt_attr("service.name", service_name); From e2fc6d97c6eb8bcfbf89a816a0c8eca1d473de25 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Mon, 9 Mar 2026 11:17:28 +0100 Subject: [PATCH 4/4] chore(otel_ctx): always set tags + small code clean-up --- libdd-library-config/src/tracer_metadata.rs | 65 +++++++++------------ 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/libdd-library-config/src/tracer_metadata.rs b/libdd-library-config/src/tracer_metadata.rs index 641e60626e..03a8089045 100644 --- a/libdd-library-config/src/tracer_metadata.rs +++ b/libdd-library-config/src/tracer_metadata.rs @@ -59,7 +59,23 @@ impl TracerMetadata { pub fn to_otel_process_ctx(&self) -> otel_proto::common::v1::ProcessContext { use otel_proto::common::v1::{any_value, AnyValue, KeyValue}; - // Every field of `self` should gets propagated to the otel context. + fn key_value(key: &'static str, val: String) -> KeyValue { + KeyValue { + key: key.to_owned(), + value: Some(AnyValue { + value: Some(any_value::Value::StringValue(val)), + }), + key_ref: 0, + } + } + + // Even if there's no value, we still set the key to let the reader know that we do support + // and emit this specific attribute, which happens to be empty in this case. + fn key_value_opt(key: &'static str, val: &Option) -> KeyValue { + key_value(key, val.as_ref().cloned().unwrap_or_default()) + } + + // Every field of `self` should get propagated to the otel context. // If you add a new field, please also add it here and as a key/value in the otel context. let TracerMetadata { // This one isn't propagated on purpose @@ -75,46 +91,23 @@ impl TracerMetadata { container_id, } = self; - fn key_value(key: &'static str, val: String) -> KeyValue { - KeyValue { - key: key.to_owned(), - value: Some(AnyValue { - value: Some(any_value::Value::StringValue(val)), - }), - key_ref: 0, - } - } - - let mut attributes = vec![ - key_value("telemetry.sdk.language", tracer_language.clone()), - key_value("telemetry.sdk.version", tracer_version.clone()), - key_value("telemetry.sdk.name", Self::OTEL_SDK_NAME.to_owned()), - key_value("host.name", hostname.clone()), - ]; - - let mut set_opt_attr = |key: &'static str, val: &Option| { - attributes.push(key_value(key, val.as_ref().cloned().unwrap_or_default())) - }; - - set_opt_attr("service.name", service_name); - set_opt_attr("service.instance.id", runtime_id); - set_opt_attr("service.version", service_version); - set_opt_attr("deployment.environment.name", service_env); - set_opt_attr("container.id", container_id); - - let extra_attributes: Vec<_> = process_tags - .as_ref() - .map(|tags| key_value("datadog.process_tags", tags.clone())) - .into_iter() - .collect(); - otel_proto::common::v1::ProcessContext { resource: Some(otel_proto::resource::v1::Resource { - attributes, + attributes: vec![ + key_value_opt("service.name", service_name), + key_value_opt("service.instance.id", runtime_id), + key_value_opt("service.version", service_version), + key_value_opt("deployment.environment.name", service_env), + key_value("telemetry.sdk.language", tracer_language.clone()), + key_value("telemetry.sdk.version", tracer_version.clone()), + key_value("telemetry.sdk.name", Self::OTEL_SDK_NAME.to_owned()), + key_value("host.name", hostname.clone()), + key_value_opt("container.id", container_id), + ], dropped_attributes_count: 0, entity_refs: vec![], }), - extra_attributes, + extra_attributes: vec![key_value_opt("datadog.process_tags", process_tags)], } } }