From 17a67679af6bce0cbaa5b02db0ad2cd03c2ccf0d Mon Sep 17 00:00:00 2001 From: Alexandre Rulleau Date: Thu, 28 May 2026 17:00:40 +0200 Subject: [PATCH 1/3] feat(sidecar): expose default_service_name for svc.* process tags Adds `ddog_sidecar_session_set_default_service_name` so tracers can signal whether DD_SERVICE was user-set or auto-resolved (and the resolved name). The sidecar stores this per-session and injects `svc.user:true` or `svc.auto:` into outgoing process-tags payloads (telemetry, remote config, runtime info), per RFC "Signal Service Name Source via Process Tags": https://docs.google.com/document/d/1c47iSTWxIOHMHfZTF2nT9xfyQaIBP9KJvI9sRn5SvpM The companion change in dd-trace-php (PR #3921) wires the new FFI and emits the per-span counterpart on traces directly. --- datadog-sidecar-ffi/src/lib.rs | 27 +++++++++++++++++++ datadog-sidecar/src/service/blocking.rs | 8 ++++++ datadog-sidecar/src/service/mod.rs | 6 +++++ datadog-sidecar/src/service/runtime_info.rs | 2 +- datadog-sidecar/src/service/sender.rs | 20 +++++++++++++- datadog-sidecar/src/service/session_info.rs | 17 +++++++++++- .../src/service/sidecar_interface.rs | 5 ++++ datadog-sidecar/src/service/sidecar_server.rs | 16 ++++++++++- datadog-sidecar/src/service/telemetry.rs | 2 +- 9 files changed, 98 insertions(+), 5 deletions(-) diff --git a/datadog-sidecar-ffi/src/lib.rs b/datadog-sidecar-ffi/src/lib.rs index 7cc6a3267d..399f1ae6b6 100644 --- a/datadog-sidecar-ffi/src/lib.rs +++ b/datadog-sidecar-ffi/src/lib.rs @@ -723,6 +723,33 @@ pub unsafe extern "C" fn ddog_sidecar_session_set_process_tags( MaybeError::None } +/// Records the source of the default service name for the session so the +/// sidecar can inject svc.user:true or svc.auto: into +/// outgoing process tag payloads. Pass an empty `default_service_name` to +/// signal the user explicitly set DD_SERVICE; pass a non-empty value (already +/// normalized via `ddog_normalize_process_tag_value`) to signal the tracer +/// auto-resolved that name. +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub unsafe extern "C" fn ddog_sidecar_session_set_default_service_name( + transport: &mut Box, + default_service_name: ffi::CharSlice, +) -> MaybeError { + let source = if default_service_name.is_empty() { + datadog_sidecar::service::ServiceNameSource::UserDefined + } else { + datadog_sidecar::service::ServiceNameSource::AutoResolved( + default_service_name.to_utf8_lossy().into_owned(), + ) + }; + try_c!(blocking::set_session_default_service_name( + transport, + Some(source), + )); + + MaybeError::None +} + #[repr(C)] pub struct TracerHeaderTags<'a> { pub lang: ffi::CharSlice<'a>, diff --git a/datadog-sidecar/src/service/blocking.rs b/datadog-sidecar/src/service/blocking.rs index 99b84af008..fb7c324d97 100644 --- a/datadog-sidecar/src/service/blocking.rs +++ b/datadog-sidecar/src/service/blocking.rs @@ -258,6 +258,14 @@ pub fn set_session_process_tags( Ok(()) } +pub fn set_session_default_service_name( + transport: &mut SidecarTransport, + service_name_source: Option, +) -> io::Result<()> { + lock_sender(transport)?.set_session_default_service_name(service_name_source); + Ok(()) +} + /// Sends a trace as bytes. pub fn send_trace_v04_bytes( transport: &mut SidecarTransport, diff --git a/datadog-sidecar/src/service/mod.rs b/datadog-sidecar/src/service/mod.rs index bc5930fc78..8810c20b44 100644 --- a/datadog-sidecar/src/service/mod.rs +++ b/datadog-sidecar/src/service/mod.rs @@ -83,3 +83,9 @@ pub enum SidecarAction { AddTelemetryMetricPoint((String, f64, Vec)), PhpComposerTelemetryFile(PathBuf), } + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub enum ServiceNameSource { + UserDefined, + AutoResolved(String), +} diff --git a/datadog-sidecar/src/service/runtime_info.rs b/datadog-sidecar/src/service/runtime_info.rs index d05de803b3..5f40004049 100644 --- a/datadog-sidecar/src/service/runtime_info.rs +++ b/datadog-sidecar/src/service/runtime_info.rs @@ -142,7 +142,7 @@ impl ActiveApplication { .expect("Expecting remote config invariants to be set early") .clone(); - let process_tags = session.process_tags.lock_or_panic().clone(); + let process_tags = session.process_tags_with_svc_source(); if *session.remote_config_enabled.lock_or_panic() { self.remote_config_guard = Some( diff --git a/datadog-sidecar/src/service/sender.rs b/datadog-sidecar/src/service/sender.rs index f3bc672fd0..91b41ee183 100644 --- a/datadog-sidecar/src/service/sender.rs +++ b/datadog-sidecar/src/service/sender.rs @@ -35,6 +35,7 @@ use tracing::trace; struct SidecarOutbox { set_session_config: Option, set_session_process_tags: Option, + set_session_default_service_name: Option, set_universal_service_tags: Option, set_request_config: Option, clear_queue_id: Option, @@ -43,10 +44,11 @@ struct SidecarOutbox { } impl SidecarOutbox { - fn slots_mut(&mut self) -> [&mut Option; 7] { + fn slots_mut(&mut self) -> [&mut Option; 8] { [ &mut self.set_session_config, &mut self.set_session_process_tags, + &mut self.set_session_default_service_name, &mut self.set_universal_service_tags, &mut self.set_request_config, &mut self.clear_queue_id, @@ -122,6 +124,9 @@ fn coalesce(outbox: &mut SidecarOutbox, incoming: SidecarInterfaceRequest) { SidecarInterfaceRequest::SetSessionProcessTags { .. } => { outbox.set_session_process_tags = Some(incoming); } + SidecarInterfaceRequest::SetSessionDefaultServiceName { .. } => { + outbox.set_session_default_service_name = Some(incoming); + } SidecarInterfaceRequest::SetUniversalServiceTags { .. } => { outbox.set_universal_service_tags = Some(incoming); } @@ -236,6 +241,19 @@ impl SidecarSender { self.try_drain_outbox(); } + pub fn set_session_default_service_name( + &mut self, + service_name_source: Option, + ) { + coalesce( + &mut self.outbox, + SidecarInterfaceRequest::SetSessionDefaultServiceName { + service_name_source, + }, + ); + self.try_drain_outbox(); + } + #[allow(clippy::too_many_arguments)] pub fn set_universal_service_tags( &mut self, diff --git a/datadog-sidecar/src/service/session_info.rs b/datadog-sidecar/src/service/session_info.rs index efa0204715..041dc6d914 100644 --- a/datadog-sidecar/src/service/session_info.rs +++ b/datadog-sidecar/src/service/session_info.rs @@ -18,7 +18,7 @@ use libdd_common::{tag::Tag, MutexExt}; use tracing::{debug, error, info, trace, warn}; use crate::service::agent_info::AgentInfoGuard; -use crate::service::{InstanceId, QueueId, RuntimeInfo}; +use crate::service::{InstanceId, QueueId, RuntimeInfo, ServiceNameSource}; /// `SessionInfo` holds information about a session. /// @@ -45,6 +45,7 @@ pub(crate) struct SessionInfo { pub(crate) pid: Arc, pub(crate) remote_config_enabled: Arc>, pub(crate) process_tags: Arc>>, + pub(crate) service_name_source: Arc>>, pub(crate) stats_config: Arc>>, } @@ -130,6 +131,20 @@ impl SessionInfo { self.runtimes.lock_or_panic() } + pub(crate) fn process_tags_with_svc_source(&self) -> Vec { + let mut tags = self.process_tags.lock_or_panic().clone(); + if let Some(source) = self.service_name_source.lock_or_panic().as_ref() { + let (key, value) = match source { + ServiceNameSource::UserDefined => ("svc.user", "true".to_string()), + ServiceNameSource::AutoResolved(name) => ("svc.auto", name.clone()), + }; + if let Ok(tag) = Tag::new(key, value) { + tags.push(tag); + } + } + tags + } + pub(crate) fn get_telemetry_config( &self, ) -> MutexGuard<'_, Option> { diff --git a/datadog-sidecar/src/service/sidecar_interface.rs b/datadog-sidecar/src/service/sidecar_interface.rs index ff6313ba36..35406c7bad 100644 --- a/datadog-sidecar/src/service/sidecar_interface.rs +++ b/datadog-sidecar/src/service/sidecar_interface.rs @@ -69,6 +69,11 @@ pub trait SidecarInterface { /// * `process_tags` - The process tags. async fn set_session_process_tags(process_tags: Vec); + /// Records the source of the default service name for the session. + async fn set_session_default_service_name( + service_name_source: Option, + ); + /// Removes the application entry for the given queue ID from the instance. /// /// # Arguments diff --git a/datadog-sidecar/src/service/sidecar_server.rs b/datadog-sidecar/src/service/sidecar_server.rs index e4841081a6..d5c258dd62 100644 --- a/datadog-sidecar/src/service/sidecar_server.rs +++ b/datadog-sidecar/src/service/sidecar_server.rs @@ -416,7 +416,7 @@ impl SidecarInterface for ConnectionSidecarHandler { .unwrap_or("unknown-service"); let env = entry.get().env.as_deref().unwrap_or("none"); - let process_tags = session.process_tags.lock_or_panic().clone(); + let process_tags = session.process_tags_with_svc_source(); // Pre-compute session config so both the primary and retry get_or_create calls // can use it without re-locking the session. @@ -764,6 +764,20 @@ impl SidecarInterface for ConnectionSidecarHandler { *session.process_tags.lock_or_panic() = process_tags; } + async fn set_session_default_service_name( + &self, + _peer: PeerCredentials, + service_name_source: Option, + ) { + let session_id = self + .session_id + .get() + .map(|s| s.as_str()) + .unwrap_or_default(); + let session = self.server.get_session(session_id); + *session.service_name_source.lock_or_panic() = service_name_source; + } + async fn shutdown_runtime(&self, _peer: PeerCredentials, instance_id: InstanceId) { let session = self.server.get_session(&instance_id.session_id); tokio::spawn(async move { session.shutdown_runtime(&instance_id.runtime_id).await }); diff --git a/datadog-sidecar/src/service/telemetry.rs b/datadog-sidecar/src/service/telemetry.rs index 201f72fb33..3896220f7d 100644 --- a/datadog-sidecar/src/service/telemetry.rs +++ b/datadog-sidecar/src/service/telemetry.rs @@ -701,7 +701,7 @@ fn get_telemetry_client( return None; }; - let process_tags = session.process_tags.lock_or_panic().clone(); + let process_tags = session.process_tags_with_svc_source(); Some(sidecar.telemetry_clients.get_or_create( service_name, From cb18140f29effc2529ef9d1f9d93f2972c98f1b4 Mon Sep 17 00:00:00 2001 From: Alexandre Rulleau Date: Thu, 28 May 2026 17:36:38 +0200 Subject: [PATCH 2/3] fix(sidecar): include svc.* in stats payloads, refresh on update Stats payloads were the one consumer not routed through `process_tags_with_svc_source()` because StatsConfig holds a pre-joined `String`. Two changes: - Build StatsConfig.process_tags from `session.process_tags_with_svc_source()` at construction time so concentrators created after the source is set carry svc.*. - Refresh StatsConfig.process_tags from both `set_session_process_tags` and `set_session_default_service_name` so live updates propagate. Addresses review feedback on initial PR. --- datadog-sidecar/src/service/session_info.rs | 11 +++++++++++ datadog-sidecar/src/service/sidecar_server.rs | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/datadog-sidecar/src/service/session_info.rs b/datadog-sidecar/src/service/session_info.rs index 041dc6d914..dee6cec678 100644 --- a/datadog-sidecar/src/service/session_info.rs +++ b/datadog-sidecar/src/service/session_info.rs @@ -145,6 +145,17 @@ impl SessionInfo { tags } + pub(crate) fn refresh_stats_process_tags(&self) { + if let Some(stats) = self.stats_config.lock_or_panic().as_mut() { + stats.process_tags = self + .process_tags_with_svc_source() + .iter() + .map(|t| t.to_string()) + .collect::>() + .join(","); + } + } + pub(crate) fn get_telemetry_config( &self, ) -> MutexGuard<'_, Option> { diff --git a/datadog-sidecar/src/service/sidecar_server.rs b/datadog-sidecar/src/service/sidecar_server.rs index d5c258dd62..45ee68f0ee 100644 --- a/datadog-sidecar/src/service/sidecar_server.rs +++ b/datadog-sidecar/src/service/sidecar_server.rs @@ -697,8 +697,8 @@ impl SidecarInterface for ConnectionSidecarHandler { } else { config.hostname.clone() }, - process_tags: config - .process_tags + process_tags: session + .process_tags_with_svc_source() .iter() .map(|t| t.to_string()) .collect::>() @@ -762,6 +762,7 @@ impl SidecarInterface for ConnectionSidecarHandler { .unwrap_or_default(); let session = self.server.get_session(session_id); *session.process_tags.lock_or_panic() = process_tags; + session.refresh_stats_process_tags(); } async fn set_session_default_service_name( @@ -776,6 +777,7 @@ impl SidecarInterface for ConnectionSidecarHandler { .unwrap_or_default(); let session = self.server.get_session(session_id); *session.service_name_source.lock_or_panic() = service_name_source; + session.refresh_stats_process_tags(); } async fn shutdown_runtime(&self, _peer: PeerCredentials, instance_id: InstanceId) { From 975e3af975322e6556c212261adc24c1708bb607 Mon Sep 17 00:00:00 2001 From: Alexandre Rulleau Date: Thu, 28 May 2026 17:43:16 +0200 Subject: [PATCH 3/3] chore(sidecar): trim FFI doc comment --- datadog-sidecar-ffi/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/datadog-sidecar-ffi/src/lib.rs b/datadog-sidecar-ffi/src/lib.rs index 399f1ae6b6..d18dd943fd 100644 --- a/datadog-sidecar-ffi/src/lib.rs +++ b/datadog-sidecar-ffi/src/lib.rs @@ -723,12 +723,9 @@ pub unsafe extern "C" fn ddog_sidecar_session_set_process_tags( MaybeError::None } -/// Records the source of the default service name for the session so the -/// sidecar can inject svc.user:true or svc.auto: into -/// outgoing process tag payloads. Pass an empty `default_service_name` to -/// signal the user explicitly set DD_SERVICE; pass a non-empty value (already -/// normalized via `ddog_normalize_process_tag_value`) to signal the tracer -/// auto-resolved that name. +/// Records the default service name for the session. Empty = user-defined +/// (sidecar emits `svc.user:true`); non-empty = auto-resolved with that name +/// (sidecar emits `svc.auto:`). #[no_mangle] #[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn ddog_sidecar_session_set_default_service_name(