use crate::trace_context;
use crate::{
Attached, Endpoint, OpenSpan, Report, Sample, SamplingFlags, Span, SpanId, SpanState,
TraceContext, TraceId,
};
use lazycell::AtomicLazyCell;
use rand::Rng;
use std::error::Error;
use std::fmt;
use std::time::{Instant, SystemTime};
pub(crate) static TRACER: AtomicLazyCell<Tracer> = AtomicLazyCell::NONE;
pub(crate) struct Tracer {
pub sampler: Box<dyn Sample + Sync + Send>,
pub reporter: Box<dyn Report + Sync + Send>,
pub local_endpoint: Endpoint,
}
pub fn set_tracer<S, R>(
sampler: S,
reporter: R,
local_endpoint: Endpoint,
) -> Result<(), SetTracerError>
where
S: Sample + 'static + Sync + Send,
R: Report + 'static + Sync + Send,
{
TRACER
.fill(Tracer {
sampler: Box::new(sampler),
reporter: Box::new(reporter),
local_endpoint,
})
.map_err(|_| SetTracerError(()))
}
#[derive(Debug)]
pub struct SetTracerError(());
impl fmt::Display for SetTracerError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str("")
}
}
impl Error for SetTracerError {}
pub fn new_trace() -> OpenSpan<Attached> {
new_trace_from(SamplingFlags::default())
}
pub fn new_trace_from(flags: SamplingFlags) -> OpenSpan<Attached> {
let id = next_id();
let context = TraceContext::builder()
.trace_id(TraceId::from(id))
.span_id(SpanId::from(id))
.sampling_flags(flags)
.build();
make_span(context, false)
}
pub fn join_trace(context: TraceContext) -> OpenSpan<Attached> {
make_span(context, true)
}
pub fn new_child(parent: TraceContext) -> OpenSpan<Attached> {
let id = next_id();
let context = TraceContext::builder()
.trace_id(parent.trace_id())
.parent_id(parent.span_id())
.span_id(SpanId::from(id))
.sampling_flags(parent.sampling_flags())
.build();
make_span(context, false)
}
pub fn next_span() -> OpenSpan<Attached> {
match crate::current() {
Some(context) => new_child(context),
None => new_trace(),
}
}
fn next_id() -> [u8; 8] {
let mut id = [0; 8];
rand::thread_rng().fill(&mut id);
id
}
fn make_span(mut context: TraceContext, mut shared: bool) -> OpenSpan<Attached> {
let tracer = match TRACER.borrow() {
Some(tracer) => tracer,
None => return OpenSpan::new(context, SpanState::Nop),
};
if context.sampled().is_none() {
context = trace_context::Builder::from(context)
.sampled(tracer.sampler.sample(context.trace_id()))
.build();
shared = false;
}
let state = match context.sampled() {
Some(false) => SpanState::Nop,
_ => {
let mut span = Span::builder();
span.trace_id(context.trace_id())
.id(context.span_id())
.timestamp(SystemTime::now())
.shared(shared)
.local_endpoint(tracer.local_endpoint.clone());
if let Some(parent_id) = context.parent_id() {
span.parent_id(parent_id);
}
SpanState::Real {
span,
start_instant: Instant::now(),
}
}
};
OpenSpan::new(context, state)
}