pub const DEFAULT_HEADER_CHAIN: &[&str] = &["x-request-id", "x-correlation-id", "traceparent"];
pub fn extract_from_headers<H, F>(headers: &H, chain: &[&str], get: F) -> Option<String>
where
F: Fn(&H, &str) -> Option<String>,
{
for &name in chain {
if let Some(value) = get(headers, name) {
let trimmed = value.trim();
if trimmed.is_empty() {
continue;
}
if name.eq_ignore_ascii_case("traceparent") {
if let Some(trace_id) = parse_traceparent_trace_id(trimmed) {
return Some(trace_id.to_owned());
}
continue;
}
return Some(trimmed.to_owned());
}
}
None
}
#[must_use]
pub fn parse_traceparent_trace_id(traceparent: &str) -> Option<&str> {
let mut parts = traceparent.split('-');
let _version = parts.next()?;
let trace_id = parts.next()?;
if trace_id.len() != 32 || !trace_id.chars().all(|c| c.is_ascii_hexdigit()) {
return None;
}
Some(trace_id)
}
pub fn with_id<F, R>(id: impl Into<String>, f: F) -> R
where
F: FnOnce() -> R,
{
let id = id.into();
let span = tracing::info_span!("correlation", correlation_id = %id);
let _enter = span.enter();
f()
}
#[must_use]
pub fn span(id: impl AsRef<str>) -> tracing::Span {
tracing::info_span!("correlation", correlation_id = %id.as_ref())
}