use crate::{
global, otel_debug,
trace::{Span, SpanContext, Status},
Context, ContextGuard, KeyValue,
};
use std::{borrow::Cow, error::Error, sync::Mutex};
pub use crate::context::{FutureExt, WithContext};
const NOOP_SPAN: SynchronizedSpan = SynchronizedSpan {
span_context: SpanContext::NONE,
inner: None,
};
#[derive(Debug)]
pub struct SpanRef<'a>(&'a SynchronizedSpan);
#[derive(Debug)]
pub(crate) struct SynchronizedSpan {
span_context: SpanContext,
inner: Option<Mutex<global::BoxedSpan>>,
}
impl SynchronizedSpan {
pub(crate) fn span_context(&self) -> &SpanContext {
&self.span_context
}
}
impl From<SpanContext> for SynchronizedSpan {
fn from(value: SpanContext) -> Self {
Self {
span_context: value,
inner: None,
}
}
}
impl<T: Span + Send + Sync + 'static> From<T> for SynchronizedSpan {
fn from(value: T) -> Self {
Self {
span_context: value.span_context().clone(),
inner: Some(Mutex::new(global::BoxedSpan::new(value))),
}
}
}
impl SpanRef<'_> {
fn with_inner_mut<F: FnOnce(&mut global::BoxedSpan)>(&self, f: F) {
if let Some(ref inner) = self.0.inner {
match inner.lock() {
Ok(mut locked) => f(&mut locked),
Err(err) => {
otel_debug!(
name: "SpanRef.LockFailed",
message = "Failed to acquire lock for SpanRef: {:?}",
reason = format!("{:?}", err),
span_context = format!("{:?}", self.0.span_context));
}
}
}
}
}
impl SpanRef<'_> {
pub fn add_event<T>(&self, name: T, attributes: Vec<KeyValue>)
where
T: Into<Cow<'static, str>>,
{
self.with_inner_mut(|inner| inner.add_event(name, attributes))
}
pub fn record_error(&self, err: &dyn Error) {
self.with_inner_mut(|inner| inner.record_error(err))
}
pub fn add_event_with_timestamp<T>(
&self,
name: T,
timestamp: std::time::SystemTime,
attributes: Vec<crate::KeyValue>,
) where
T: Into<Cow<'static, str>>,
{
self.with_inner_mut(move |inner| {
inner.add_event_with_timestamp(name, timestamp, attributes)
})
}
pub fn span_context(&self) -> &SpanContext {
&self.0.span_context
}
pub fn is_recording(&self) -> bool {
self.0
.inner
.as_ref()
.and_then(|inner| inner.lock().ok().map(|active| active.is_recording()))
.unwrap_or(false)
}
pub fn set_attribute(&self, attribute: crate::KeyValue) {
self.with_inner_mut(move |inner| inner.set_attribute(attribute))
}
pub fn set_attributes(&self, attributes: impl IntoIterator<Item = KeyValue>) {
self.with_inner_mut(move |inner| inner.set_attributes(attributes))
}
pub fn set_status(&self, status: Status) {
self.with_inner_mut(move |inner| inner.set_status(status))
}
pub fn update_name<T>(&self, new_name: T)
where
T: Into<Cow<'static, str>>,
{
self.with_inner_mut(move |inner| inner.update_name(new_name))
}
pub fn add_link(&self, span_context: SpanContext, attributes: Vec<KeyValue>) {
self.with_inner_mut(move |inner| inner.add_link(span_context, attributes));
}
pub fn end(&self) {
self.end_with_timestamp(crate::time::now());
}
pub fn end_with_timestamp(&self, timestamp: std::time::SystemTime) {
self.with_inner_mut(move |inner| inner.end_with_timestamp(timestamp))
}
}
pub trait TraceContextExt {
fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self;
fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self;
fn span(&self) -> SpanRef<'_>;
fn has_active_span(&self) -> bool;
fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self;
}
impl TraceContextExt for Context {
fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self {
Context::current_with_synchronized_span(span.into())
}
fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self {
self.with_synchronized_span(span.into())
}
fn span(&self) -> SpanRef<'_> {
if let Some(span) = self.span.as_ref() {
SpanRef(span)
} else {
SpanRef(&NOOP_SPAN)
}
}
fn has_active_span(&self) -> bool {
self.span.is_some()
}
fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {
self.with_synchronized_span(span_context.into())
}
}
#[must_use = "Dropping the guard detaches the context."]
pub fn mark_span_as_active<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> ContextGuard {
let cx = Context::current_with_span(span);
cx.attach()
}
pub fn get_active_span<F, T>(f: F) -> T
where
F: FnOnce(SpanRef<'_>) -> T,
{
Context::map_current(|cx| f(cx.span()))
}