use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use fastant::Instant;
use crate::collector::CollectToken;
use crate::local::local_span_stack::LOCAL_SPAN_STACK;
use crate::local::local_span_stack::LocalSpanStack;
use crate::local::local_span_stack::SpanLineHandle;
use crate::prelude::SpanContext;
use crate::prelude::SpanRecord;
use crate::util::RawSpans;
#[must_use]
#[derive(Default)]
pub struct LocalCollector {
#[cfg(feature = "enable")]
inner: Option<LocalCollectorInner>,
}
struct LocalCollectorInner {
stack: Rc<RefCell<LocalSpanStack>>,
span_line_handle: SpanLineHandle,
}
#[derive(Debug, Clone)]
pub struct LocalSpans {
#[cfg(feature = "enable")]
pub(crate) inner: Arc<LocalSpansInner>,
}
#[derive(Debug)]
pub struct LocalSpansInner {
pub spans: RawSpans,
pub end_time: Instant,
}
impl LocalCollector {
pub fn start() -> Self {
#[cfg(not(feature = "enable"))]
{
LocalCollector::default()
}
#[cfg(feature = "enable")]
{
LOCAL_SPAN_STACK
.try_with(|stack| Self::new(None, stack.clone()))
.unwrap_or_default()
}
}
pub fn collect(self) -> LocalSpans {
#[cfg(not(feature = "enable"))]
{
LocalSpans {}
}
#[cfg(feature = "enable")]
{
LocalSpans {
inner: Arc::new(self.collect_spans_and_token().0),
}
}
}
}
#[cfg(feature = "enable")]
impl LocalCollector {
pub(crate) fn new(
collect_token: Option<CollectToken>,
stack: Rc<RefCell<LocalSpanStack>>,
) -> Self {
let span_line_epoch = {
let stack = &mut (*stack).borrow_mut();
stack.register_span_line(collect_token)
};
Self {
inner: span_line_epoch.map(move |span_line_handle| LocalCollectorInner {
stack,
span_line_handle,
}),
}
}
pub(crate) fn collect_spans_and_token(mut self) -> (LocalSpansInner, Option<CollectToken>) {
let (spans, collect_token) = self
.inner
.take()
.and_then(
|LocalCollectorInner {
stack,
span_line_handle,
}| {
let s = &mut (*stack).borrow_mut();
s.unregister_and_collect(span_line_handle)
},
)
.unwrap_or_default();
(
LocalSpansInner {
spans,
end_time: Instant::now(),
},
collect_token,
)
}
}
impl Drop for LocalCollector {
fn drop(&mut self) {
#[cfg(feature = "enable")]
if let Some(LocalCollectorInner {
stack,
span_line_handle,
}) = self.inner.take()
{
let s = &mut (*stack).borrow_mut();
let _ = s.unregister_and_collect(span_line_handle);
}
}
}
impl LocalSpans {
pub fn to_span_records(&self, parent: SpanContext) -> Vec<SpanRecord> {
#[cfg(not(feature = "enable"))]
{
vec![]
}
#[cfg(feature = "enable")]
{
self.inner.to_span_records(parent)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collector::CollectToken;
use crate::collector::SpanId;
use crate::prelude::LocalSpan;
use crate::prelude::TraceId;
use crate::util::tree::tree_str_from_raw_spans;
use crate::util::tree::tree_str_from_span_records;
#[test]
fn local_collector_basic() {
let stack = Rc::new(RefCell::new(LocalSpanStack::with_capacity(16)));
let collector1 = LocalCollector::new(None, stack.clone());
let span1 = stack.borrow_mut().enter_span("span1").unwrap();
{
let token2 = CollectToken {
trace_id: TraceId(1234),
parent_id: SpanId::default(),
collect_id: 42,
is_root: false,
is_sampled: true,
};
let collector2 = LocalCollector::new(Some(token2), stack.clone());
let span2 = stack.borrow_mut().enter_span("span2").unwrap();
let span3 = stack.borrow_mut().enter_span("span3").unwrap();
stack.borrow_mut().exit_span(span3);
stack.borrow_mut().exit_span(span2);
let (spans, token) = collector2.collect_spans_and_token();
assert_eq!(token.unwrap(), token2);
assert_eq!(
tree_str_from_raw_spans(spans.spans),
r"
span2 []
span3 []
"
);
}
stack.borrow_mut().exit_span(span1);
let spans = collector1.collect();
assert_eq!(
tree_str_from_raw_spans(spans.inner.spans.clone()),
r"
span1 []
"
);
}
#[test]
fn drop_without_collect() {
let stack = Rc::new(RefCell::new(LocalSpanStack::with_capacity(16)));
let collector1 = LocalCollector::new(None, stack.clone());
let span1 = stack.borrow_mut().enter_span("span1").unwrap();
{
let token2 = CollectToken {
trace_id: TraceId(1234),
parent_id: SpanId::default(),
collect_id: 42,
is_root: false,
is_sampled: true,
};
let collector2 = LocalCollector::new(Some(token2), stack.clone());
let span2 = stack.borrow_mut().enter_span("span2").unwrap();
let span3 = stack.borrow_mut().enter_span("span3").unwrap();
stack.borrow_mut().exit_span(span3);
stack.borrow_mut().exit_span(span2);
drop(collector2);
}
stack.borrow_mut().exit_span(span1);
let spans = collector1.collect();
assert_eq!(
tree_str_from_raw_spans(spans.inner.spans.clone()),
r"
span1 []
"
);
}
#[test]
fn local_spans_to_span_record() {
let collector = LocalCollector::start();
let span1 = LocalSpan::enter_with_local_parent("span1").with_property(|| ("k1", "v1"));
let span2 = LocalSpan::enter_with_local_parent("span2").with_property(|| ("k2", "v2"));
drop(span2);
drop(span1);
let local_spans: LocalSpans = collector.collect();
let parent_context = SpanContext::random();
let span_records = local_spans.to_span_records(parent_context);
assert_eq!(
tree_str_from_span_records(span_records),
r#"
span1 [("k1", "v1")]
span2 [("k2", "v2")]
"#
);
}
}