use crate::trace::{noop::NoopTracerProvider, SpanContext, Status};
use crate::{otel_error, otel_info, InstrumentationScope};
use crate::{trace, trace::TracerProvider, Context, KeyValue};
use std::borrow::Cow;
use std::fmt;
use std::sync::{Arc, OnceLock, RwLock};
use std::time::SystemTime;
pub trait ObjectSafeSpan {
fn add_event_with_timestamp(
&mut self,
name: Cow<'static, str>,
timestamp: SystemTime,
attributes: Vec<KeyValue>,
);
fn span_context(&self) -> &SpanContext;
fn is_recording(&self) -> bool;
fn set_attribute(&mut self, attribute: KeyValue);
fn set_status(&mut self, status: Status);
fn update_name(&mut self, new_name: Cow<'static, str>);
fn add_link(&mut self, span_context: SpanContext, attributes: Vec<KeyValue>);
fn end(&mut self) {
self.end_with_timestamp(crate::time::now());
}
fn end_with_timestamp(&mut self, timestamp: SystemTime);
}
impl<T: trace::Span> ObjectSafeSpan for T {
fn add_event_with_timestamp(
&mut self,
name: Cow<'static, str>,
timestamp: SystemTime,
attributes: Vec<KeyValue>,
) {
self.add_event_with_timestamp(name, timestamp, attributes)
}
fn span_context(&self) -> &SpanContext {
self.span_context()
}
fn is_recording(&self) -> bool {
self.is_recording()
}
fn set_attribute(&mut self, attribute: KeyValue) {
self.set_attribute(attribute)
}
fn set_status(&mut self, status: Status) {
self.set_status(status)
}
fn update_name(&mut self, new_name: Cow<'static, str>) {
self.update_name(new_name)
}
fn add_link(&mut self, span_context: SpanContext, attributes: Vec<KeyValue>) {
self.add_link(span_context, attributes)
}
fn end_with_timestamp(&mut self, timestamp: SystemTime) {
self.end_with_timestamp(timestamp)
}
}
pub struct BoxedSpan(Box<dyn ObjectSafeSpan + Send + Sync>);
impl BoxedSpan {
pub(crate) fn new<T>(span: T) -> Self
where
T: ObjectSafeSpan + Send + Sync + 'static,
{
BoxedSpan(Box::new(span))
}
}
impl fmt::Debug for BoxedSpan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BoxedSpan")
}
}
impl trace::Span for BoxedSpan {
fn add_event_with_timestamp<T>(
&mut self,
name: T,
timestamp: SystemTime,
attributes: Vec<KeyValue>,
) where
T: Into<Cow<'static, str>>,
{
self.0
.add_event_with_timestamp(name.into(), timestamp, attributes)
}
fn span_context(&self) -> &trace::SpanContext {
self.0.span_context()
}
fn is_recording(&self) -> bool {
self.0.is_recording()
}
fn set_attribute(&mut self, attribute: KeyValue) {
self.0.set_attribute(attribute)
}
fn set_status(&mut self, status: trace::Status) {
self.0.set_status(status)
}
fn update_name<T>(&mut self, new_name: T)
where
T: Into<Cow<'static, str>>,
{
self.0.update_name(new_name.into())
}
fn add_link(&mut self, span_context: trace::SpanContext, attributes: Vec<KeyValue>) {
self.0.add_link(span_context, attributes)
}
fn end_with_timestamp(&mut self, timestamp: SystemTime) {
self.0.end_with_timestamp(timestamp);
}
}
pub struct BoxedTracer(Box<dyn ObjectSafeTracer + Send + Sync>);
impl BoxedTracer {
pub fn new(tracer: Box<dyn ObjectSafeTracer + Send + Sync>) -> Self {
BoxedTracer(tracer)
}
}
impl fmt::Debug for BoxedTracer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BoxedTracer")
}
}
impl trace::Tracer for BoxedTracer {
type Span = BoxedSpan;
fn build_with_context(&self, builder: trace::SpanBuilder, parent_cx: &Context) -> Self::Span {
BoxedSpan(self.0.build_with_context_boxed(builder, parent_cx))
}
}
pub trait ObjectSafeTracer {
fn build_with_context_boxed(
&self,
builder: trace::SpanBuilder,
parent_cx: &Context,
) -> Box<dyn ObjectSafeSpan + Send + Sync>;
}
impl<S, T> ObjectSafeTracer for T
where
S: trace::Span + Send + Sync + 'static,
T: trace::Tracer<Span = S>,
{
fn build_with_context_boxed(
&self,
builder: trace::SpanBuilder,
parent_cx: &Context,
) -> Box<dyn ObjectSafeSpan + Send + Sync> {
Box::new(self.build_with_context(builder, parent_cx))
}
}
pub trait ObjectSafeTracerProvider {
fn boxed_tracer(&self, scope: InstrumentationScope) -> Box<dyn ObjectSafeTracer + Send + Sync>;
}
impl<S, T, P> ObjectSafeTracerProvider for P
where
S: trace::Span + Send + Sync + 'static,
T: trace::Tracer<Span = S> + Send + Sync + 'static,
P: trace::TracerProvider<Tracer = T>,
{
fn boxed_tracer(&self, scope: InstrumentationScope) -> Box<dyn ObjectSafeTracer + Send + Sync> {
Box::new(self.tracer_with_scope(scope))
}
}
#[derive(Clone)]
pub struct GlobalTracerProvider {
provider: Arc<dyn ObjectSafeTracerProvider + Send + Sync>,
}
impl fmt::Debug for GlobalTracerProvider {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GlobalTracerProvider")
}
}
impl GlobalTracerProvider {
fn new<P, T, S>(provider: P) -> Self
where
S: trace::Span + Send + Sync + 'static,
T: trace::Tracer<Span = S> + Send + Sync + 'static,
P: trace::TracerProvider<Tracer = T> + Send + Sync + 'static,
{
GlobalTracerProvider {
provider: Arc::new(provider),
}
}
}
impl trace::TracerProvider for GlobalTracerProvider {
type Tracer = BoxedTracer;
fn tracer_with_scope(&self, scope: InstrumentationScope) -> Self::Tracer {
BoxedTracer(self.provider.boxed_tracer(scope))
}
}
static GLOBAL_TRACER_PROVIDER: OnceLock<RwLock<GlobalTracerProvider>> = OnceLock::new();
#[inline]
fn global_tracer_provider() -> &'static RwLock<GlobalTracerProvider> {
GLOBAL_TRACER_PROVIDER
.get_or_init(|| RwLock::new(GlobalTracerProvider::new(NoopTracerProvider::new())))
}
pub fn tracer_provider() -> GlobalTracerProvider {
let global_provider = global_tracer_provider().read();
if let Ok(provider) = global_provider {
provider.clone()
} else {
otel_error!(name: "TracerProvider.GlobalGetFailed", message = "Getting global tracer provider failed. Traces created using global::tracer() or global::tracer_with_scope() will not function. Report this issue in OpenTelemetry repo.");
GlobalTracerProvider::new(NoopTracerProvider::new())
}
}
pub fn tracer(name: impl Into<Cow<'static, str>>) -> BoxedTracer {
tracer_provider().tracer(name.into())
}
pub fn tracer_with_scope(scope: InstrumentationScope) -> BoxedTracer {
tracer_provider().tracer_with_scope(scope)
}
pub fn set_tracer_provider<P, T, S>(new_provider: P)
where
S: trace::Span + Send + Sync + 'static,
T: trace::Tracer<Span = S> + Send + Sync + 'static,
P: trace::TracerProvider<Tracer = T> + Send + Sync + 'static,
{
let mut global_provider = global_tracer_provider().write();
if let Ok(ref mut provider) = global_provider {
**provider = GlobalTracerProvider::new(new_provider);
otel_info!(name: "TracerProvider.GlobalSet", message = "Global tracer provider is set. Traces can now be created using global::tracer() or global::tracer_with_scope().");
} else {
otel_error!(name: "TracerProvider.GlobalSetFailed", message = "Setting global tracer provider failed. Traces created using global::tracer() or global::tracer_with_scope() will not function. Report this issue in OpenTelemetry repo.");
}
}