use std::ffi::{CStr, CString};
use std::sync::OnceLock;
use libdd_data_pipeline::trace_exporter::TelemetryInstrumentationSessions;
const ENV_ROOT_RS_SESSION_ID: &str = "_DD_ROOT_RS_SESSION_ID";
const ENV_PARENT_RS_SESSION_ID: &str = "_DD_PARENT_RS_SESSION_ID";
static CAPTURED: OnceLock<TelemetryInstrumentationSessions> = OnceLock::new();
pub(crate) fn sessions_from_runtime_id(runtime_id: &str) -> TelemetryInstrumentationSessions {
if let Some(cached) = CAPTURED.get() {
if cached.session_id.as_deref() == Some(runtime_id) {
return cached.clone();
}
}
sessions_from_env(runtime_id, fallback_read_env)
}
#[allow(clippy::disallowed_methods)]
fn fallback_read_env(key: &str) -> Option<String> {
std::env::var(key).ok()
}
#[ctor::ctor]
fn install_lineage() {
let _ = std::panic::catch_unwind(install_lineage_inner);
}
fn install_lineage_inner() {
let runtime_id = crate::core::configuration::Config::process_runtime_id();
let captured = sessions_from_env(runtime_id, |k| unsafe { libc_getenv(k) });
let _ = CAPTURED.set(captured);
unsafe { install_outbound_env_libc(runtime_id) };
}
unsafe fn libc_getenv(key: &str) -> Option<String> {
let key_c = CString::new(key).ok()?;
let ptr = unsafe { libc::getenv(key_c.as_ptr()) };
if ptr.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned(),
)
}
unsafe fn install_outbound_env_libc(runtime_id: &str) {
let Ok(rid_c) = CString::new(runtime_id) else {
return;
};
let root_c = c"_DD_ROOT_RS_SESSION_ID";
let parent_c = c"_DD_PARENT_RS_SESSION_ID";
if unsafe { libc::getenv(root_c.as_ptr()) }.is_null() {
unsafe { ll_setenv(root_c, rid_c.as_c_str()) };
}
unsafe { ll_setenv(parent_c, rid_c.as_c_str()) };
}
#[cfg(unix)]
unsafe fn ll_setenv(key: &CStr, value: &CStr) {
unsafe { libc::setenv(key.as_ptr(), value.as_ptr(), 1) };
}
#[cfg(windows)]
unsafe fn ll_setenv(key: &CStr, value: &CStr) {
unsafe { libc::putenv_s(key.as_ptr(), value.as_ptr()) };
}
fn sessions_from_env<F>(runtime_id: &str, get_env: F) -> TelemetryInstrumentationSessions
where
F: Fn(&str) -> Option<String>,
{
let session_id = runtime_id.to_owned();
TelemetryInstrumentationSessions {
root_session_id: get_env(ENV_ROOT_RS_SESSION_ID).filter(|r| r != &session_id),
parent_session_id: get_env(ENV_PARENT_RS_SESSION_ID).filter(|p| p != &session_id),
session_id: Some(session_id),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
fn env_map(items: &[(&str, &str)]) -> impl Fn(&str) -> Option<String> {
let map: HashMap<String, String> = items
.iter()
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
.collect();
move |k| map.get(k).cloned()
}
#[test]
fn no_lineage_when_env_unset() {
let s = sessions_from_env("rid-a", env_map(&[]));
assert_eq!(s.session_id.as_deref(), Some("rid-a"));
assert!(s.root_session_id.is_none());
assert!(s.parent_session_id.is_none());
}
#[test]
fn drops_lineage_matching_runtime_id() {
let s = sessions_from_env(
"current",
env_map(&[
(ENV_ROOT_RS_SESSION_ID, "current"),
(ENV_PARENT_RS_SESSION_ID, "current"),
]),
);
assert!(s.root_session_id.is_none());
assert!(s.parent_session_id.is_none());
}
#[test]
fn captures_distinct_lineage() {
let s = sessions_from_env(
"current",
env_map(&[
(ENV_ROOT_RS_SESSION_ID, "root-x"),
(ENV_PARENT_RS_SESSION_ID, "parent-x"),
]),
);
assert_eq!(s.session_id.as_deref(), Some("current"));
assert_eq!(s.root_session_id.as_deref(), Some("root-x"));
assert_eq!(s.parent_session_id.as_deref(), Some("parent-x"));
}
}