use crate::global::handle_error;
use crate::trace::{noop::NoopTracerProvider, SpanContext, StatusCode, TraceResult};
use crate::{trace, trace::TracerProvider, Context, KeyValue};
use std::borrow::Cow;
use std::fmt;
use std::mem;
use std::sync::{Arc, 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, code: StatusCode, message: String);
fn update_name(&mut self, new_name: Cow<'static, str>);
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, code: StatusCode, message: String) {
self.set_status(code, message)
}
fn update_name(&mut self, new_name: Cow<'static, str>) {
self.update_name(new_name)
}
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, code: trace::StatusCode, message: String) {
self.0.set_status(code, message)
}
fn update_name<T>(&mut self, new_name: T)
where
T: Into<Cow<'static, str>>,
{
self.0.update_name(new_name.into())
}
fn end_with_timestamp(&mut self, timestamp: SystemTime) {
self.0.end_with_timestamp(timestamp);
}
}
pub struct BoxedTracer(Box<dyn ObjectSafeTracer + Send + Sync>);
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 start_with_context<T>(&self, name: T, parent_cx: &Context) -> Self::Span
where
T: Into<Cow<'static, str>>,
{
BoxedSpan(self.0.start_with_context_boxed(name.into(), parent_cx))
}
fn span_builder<T>(&self, name: T) -> trace::SpanBuilder
where
T: Into<Cow<'static, str>>,
{
trace::SpanBuilder::from_name(name)
}
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 start_with_context_boxed(
&self,
name: Cow<'static, str>,
parent_cx: &Context,
) -> Box<dyn ObjectSafeSpan + Send + Sync>;
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 start_with_context_boxed(
&self,
name: Cow<'static, str>,
parent_cx: &Context,
) -> Box<dyn ObjectSafeSpan + Send + Sync> {
Box::new(self.start_with_context(name, parent_cx))
}
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 versioned_tracer_boxed(
&self,
name: Cow<'static, str>,
version: Option<&'static str>,
schema_url: Option<&'static str>,
) -> Box<dyn ObjectSafeTracer + Send + Sync>;
fn force_flush(&self) -> Vec<TraceResult<()>>;
}
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 versioned_tracer_boxed(
&self,
name: Cow<'static, str>,
version: Option<&'static str>,
schema_url: Option<&'static str>,
) -> Box<dyn ObjectSafeTracer + Send + Sync> {
Box::new(self.versioned_tracer(name, version, schema_url))
}
fn force_flush(&self) -> Vec<TraceResult<()>> {
self.force_flush()
}
}
#[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 versioned_tracer(
&self,
name: impl Into<Cow<'static, str>>,
version: Option<&'static str>,
schema_url: Option<&'static str>,
) -> Self::Tracer {
BoxedTracer(
self.provider
.versioned_tracer_boxed(name.into(), version, schema_url),
)
}
fn force_flush(&self) -> Vec<TraceResult<()>> {
self.provider.force_flush()
}
}
lazy_static::lazy_static! {
static ref GLOBAL_TRACER_PROVIDER: RwLock<GlobalTracerProvider> = RwLock::new(GlobalTracerProvider::new(trace::noop::NoopTracerProvider::new()));
}
pub fn tracer_provider() -> GlobalTracerProvider {
GLOBAL_TRACER_PROVIDER
.read()
.expect("GLOBAL_TRACER_PROVIDER RwLock poisoned")
.clone()
}
pub fn tracer(name: impl Into<Cow<'static, str>>) -> BoxedTracer {
tracer_provider().tracer(name.into())
}
pub fn set_tracer_provider<P, T, S>(new_provider: P) -> GlobalTracerProvider
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 tracer_provider = GLOBAL_TRACER_PROVIDER
.write()
.expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
mem::replace(
&mut *tracer_provider,
GlobalTracerProvider::new(new_provider),
)
}
pub fn shutdown_tracer_provider() {
let mut tracer_provider = GLOBAL_TRACER_PROVIDER
.write()
.expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
let _ = mem::replace(
&mut *tracer_provider,
GlobalTracerProvider::new(NoopTracerProvider::new()),
);
}
pub fn force_flush_tracer_provider() {
let tracer_provider = GLOBAL_TRACER_PROVIDER
.write()
.expect("GLOBAL_TRACER_PROVIDER RwLock poisoned");
let results = trace::TracerProvider::force_flush(&*tracer_provider);
for result in results {
if let Err(err) = result {
handle_error(err)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
use crate::runtime;
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
use crate::sdk::trace::TraceRuntime;
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
use crate::trace::Tracer;
use std::{fmt::Debug, io::Write, sync::Mutex};
#[derive(Debug)]
struct AssertWriter {
buf: Arc<Mutex<Vec<u8>>>,
}
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
impl AssertWriter {
fn new() -> AssertWriter {
AssertWriter {
buf: Arc::new(Mutex::new(Vec::new())),
}
}
fn len(&self) -> usize {
self.buf
.lock()
.expect("cannot acquire the lock of assert writer")
.len()
}
}
impl Write for AssertWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut buffer = self
.buf
.lock()
.expect("cannot acquire the lock of assert writer");
buffer.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
let mut buffer = self
.buf
.lock()
.expect("cannot acquire the lock of assert writer");
buffer.flush()
}
}
impl Clone for AssertWriter {
fn clone(&self) -> Self {
AssertWriter {
buf: self.buf.clone(),
}
}
}
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
fn build_batch_tracer_provider<R: TraceRuntime>(
assert_writer: AssertWriter,
runtime: R,
) -> crate::sdk::trace::TracerProvider {
use crate::sdk::trace::TracerProvider;
let exporter = crate::sdk::export::trace::stdout::Exporter::new(assert_writer, true);
TracerProvider::builder()
.with_batch_exporter(exporter, runtime)
.build()
}
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
fn build_simple_tracer_provider(
assert_writer: AssertWriter,
) -> crate::sdk::trace::TracerProvider {
use crate::sdk::trace::TracerProvider;
let exporter = crate::sdk::export::trace::stdout::Exporter::new(assert_writer, true);
TracerProvider::builder()
.with_simple_exporter(exporter)
.build()
}
#[cfg(any(feature = "rt-tokio", feature = "rt-tokio-current-thread"))]
async fn test_set_provider_in_tokio<R: TraceRuntime>(runtime: R) -> AssertWriter {
let buffer = AssertWriter::new();
let _ = set_tracer_provider(build_batch_tracer_provider(buffer.clone(), runtime));
let tracer = tracer("opentelemetery");
tracer.in_span("test", |_cx| {});
buffer
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore = "requires --test-threads=1"]
#[cfg(feature = "rt-tokio")]
async fn test_set_provider_multiple_thread_tokio() {
let assert_writer = test_set_provider_in_tokio(runtime::Tokio).await;
assert_eq!(assert_writer.len(), 0);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[ignore = "requires --test-threads=1"]
#[cfg(feature = "rt-tokio")]
async fn test_set_provider_multiple_thread_tokio_shutdown() {
let assert_writer = test_set_provider_in_tokio(runtime::Tokio).await;
shutdown_tracer_provider();
assert!(assert_writer.len() > 0);
}
#[tokio::test]
#[ignore = "requires --test-threads=1"]
#[cfg(feature = "rt-tokio")]
async fn test_set_provider_single_thread_tokio_with_simple_processor() {
let assert_writer = AssertWriter::new();
let _ = set_tracer_provider(build_simple_tracer_provider(assert_writer.clone()));
let tracer = tracer("opentelemetry");
tracer.in_span("test", |_cx| {});
shutdown_tracer_provider();
assert!(assert_writer.len() > 0);
}
#[tokio::test]
#[ignore = "requires --test-threads=1"]
#[cfg(feature = "rt-tokio-current-thread")]
async fn test_set_provider_single_thread_tokio() {
let assert_writer = test_set_provider_in_tokio(runtime::TokioCurrentThread).await;
assert_eq!(assert_writer.len(), 0)
}
#[tokio::test]
#[ignore = "requires --test-threads=1"]
#[cfg(feature = "rt-tokio-current-thread")]
async fn test_set_provider_single_thread_tokio_shutdown() {
let assert_writer = test_set_provider_in_tokio(runtime::TokioCurrentThread).await;
shutdown_tracer_provider();
assert!(assert_writer.len() > 0);
}
}