tracelite/
default_tracer.rs

1use crate::clocks::Clock;
2use crate::id_generators::IdGenerator;
3use crate::sampling::{Sampler, SamplingDecision, SamplingResult, StaticSampler};
4use crate::span_collections::SpanCollection;
5use crate::spinlock::Spinlock;
6use crate::{tracer::*, Severity};
7
8fn default_instrumentation_error_handler(err: InstrumentationError){
9    eprintln!("[ERROR] tracelite: instrumentation error: {err}");
10}
11
12pub struct DefaultTracerConfig<C, IG, SS, S, SC, X> {
13    clock: C,
14    id_generator: IG,
15    static_sampler: SS,
16    sampler: S,
17    collection: SC,
18    export_sink: X,
19    default_span_kind: Option<SpanKind>,
20    on_instrumentation_error: Box<dyn Fn(InstrumentationError) + Send + Sync>
21}
22
23impl<C, IG, SS, S, SC, X> DefaultTracerConfig<C, IG, SS, S, SC, X>
24    where C: Clock, IG: IdGenerator, SS: StaticSampler, S: Sampler, SC: SpanCollection, X: Fn(SC::Exportable) + Send + Sync + 'static
25{
26    pub fn new(clock: C, id_generator: IG, static_sampler: SS, sampler: S, collection: SC, export_sink: X) -> Self {
27        Self{
28            clock,
29            id_generator,
30            static_sampler,
31            sampler,
32            collection,
33            export_sink,
34            default_span_kind: None,
35            on_instrumentation_error: Box::new(default_instrumentation_error_handler),
36        }
37    }
38
39    pub fn install(self){
40        let tracer = DefaultTracer{
41            clock: self.clock,
42            id_generator: self.id_generator,
43            static_sampler: self.static_sampler,
44            sampler: self.sampler,
45            spans: Spinlock::new(self.collection),
46            export_sink: self.export_sink,
47            default_span_kind: self.default_span_kind,
48            on_instrumentation_error: self.on_instrumentation_error
49        };
50        globals::set_tracer(Box::new(tracer));
51    }
52
53    pub fn sampler<S2>(self, sampler: S2) -> DefaultTracerConfig<C, IG, SS, S2, SC, X> {
54        DefaultTracerConfig{
55            clock: self.clock,
56            id_generator: self.id_generator,
57            static_sampler: self.static_sampler,
58            sampler,
59            collection: self.collection,
60            export_sink: self.export_sink,
61            default_span_kind: self.default_span_kind,
62            on_instrumentation_error: self.on_instrumentation_error
63        }
64    }
65
66    pub fn default_span_kind(self, kind: SpanKind) -> Self {
67        Self{ default_span_kind: Some(kind), ..self }
68    }
69
70    pub fn on_instrumentation_error(self, f: impl Fn(InstrumentationError) + Send + Sync + 'static) -> Self {
71        Self{ on_instrumentation_error: Box::new(f), ..self }
72    }
73}
74
75pub struct DefaultTracer<C, IG, SS, S, SC, X> {
76    clock: C,
77    id_generator: IG,
78    static_sampler: SS,
79    sampler: S,
80    // NOTE this gets indexed by SpanContext::collect_idx
81    spans: Spinlock<SC>,
82    export_sink: X,
83    default_span_kind: Option<SpanKind>,
84    on_instrumentation_error: Box<dyn Fn(InstrumentationError) + Send + Sync>
85}
86
87impl<C, IG, SS, S, SC, X> Tracer for DefaultTracer<C, IG, SS, S, SC, X>
88    where C: Clock, IG: IdGenerator, SS: StaticSampler, S: Sampler, SC: SpanCollection, X: Fn(SC::Exportable) + Send + Sync + 'static
89{
90    fn is_enabled(&self, target: Option<&str>, severity: Option<Severity>) -> bool {
91        self.static_sampler.is_enabled(target, severity)
92    }
93
94    fn start_span(&self, mut args: SpanArgs, _: &mut PrivateMarker) -> Option<(RecordingSpanContext, SamplingResult)> {
95        let sampling = self.sampler.should_sample(&args);
96        match sampling.decision {
97            SamplingDecision::Drop => return None,
98            SamplingDecision::RecordOnly => {}, // TODO mark as sampled=false
99            SamplingDecision::RecordAndSample => {},
100        }
101
102        // set default span kind
103        args.kind = args.kind.or(self.default_span_kind);
104
105        // generate ids; extract trace id if there is a parent
106        let trace_id = args.parent.as_ref()
107            .map(|p| p.trace_id)
108            .unwrap_or_else(|| self.id_generator.new_trace_id());
109        let span_id = self.id_generator.new_span_id();
110
111        let span_context = SpanContext{ trace_id, span_id, trace_flags: 1 };
112        let opened_at = self.clock.now_unix_nano();
113
114        // create Span in SpanCollection
115        let Some(collect_idx) = self.spans.lock().open_span(trace_id, span_id, args, opened_at).ok() else {
116            todo!() // out of memory, what to do now?
117        };
118
119        Some((RecordingSpanContext{ span_context, collect_idx }, sampling))
120    }
121
122    fn set_attributes(&self, span: RecordingSpanContext, attrs: AttributeList) {
123        let _result = self.spans.lock().set_attributes(span.collect_idx, attrs); // TODO handle err
124    }
125
126    fn add_event(&self, span: RecordingSpanContext, args: EventArgs, _: &mut PrivateMarker) {
127        let occurs_at = self.clock.now_unix_nano();
128        let _result = self.spans.lock().add_event(span.collect_idx, args, occurs_at); // TODO handle err
129    }
130
131    fn set_status(&self, span: RecordingSpanContext, status: SpanStatus) {
132        let _result = self.spans.lock().set_status(span.collect_idx, status); // TODO handle err
133    }
134
135    fn drop_span(&self, span: RecordingSpanContext, _ : &mut PrivateMarker) {
136        let dropped_at = self.clock.now_unix_nano();
137        self.spans.lock().drop_span(span.collect_idx, dropped_at, &self.export_sink);
138    }
139
140    fn flush(&self) {
141        println!("[DEBUG] tracelite: flush");
142        self.spans.lock().flush(&self.export_sink);
143    }
144
145    fn instrumentation_error(&self, err: InstrumentationError) {
146        (self.on_instrumentation_error)(err)
147    }
148}