use crate::log::common::*;
use crate::log::writer::run_writer;
use crate::log::SerializationMethod;
use crossbeam::{bounded, Sender};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::Write;
use std::mem::ManuallyDrop;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use tracing::span::{Attributes, Record};
use tracing::Id;
use tracing_core::field::Visit;
use tracing_core::{Event, Field, Level, Metadata};
thread_local! {
static CURRENT_SPAN: RefCell<Option<Id>> = RefCell::new(None);
}
#[derive(Clone)]
pub struct Subscriber {
inner: Arc<SubscriberData>,
}
struct SubscriberData {
current_span_id: AtomicU64,
level: Level,
background_sender: ManuallyDrop<Sender<Command>>, background_thread: ManuallyDrop<JoinHandle<()>>, span_parents: Mutex<HashMap<u64, u64>>,
}
impl Subscriber {
pub fn new(dest: impl Write + Send + 'static, level: Level, method: SerializationMethod) -> Self {
let (sender, receiver) = bounded(1024_usize);
let handle = std::thread::spawn(move || {
run_writer(&receiver, dest, &method);
});
Self {
inner: Arc::new(SubscriberData {
current_span_id: AtomicU64::new(1),
level,
background_thread: ManuallyDrop::new(handle),
background_sender: ManuallyDrop::new(sender),
span_parents: Mutex::new(HashMap::new()),
}),
}
}
fn add_parent(&self, parent: Id, child: Id) {
self.inner
.span_parents
.lock()
.expect("Need to lock parental_relationship map")
.insert(child.into_u64(), parent.into_u64());
self.inner
.background_sender
.send(Command::from_data(CommandData::RecordRelationship { parent, child }))
.expect("Cannot send parent to logger");
}
}
impl Drop for SubscriberData {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.background_sender);
}
let thread = unsafe { ManuallyDrop::take(&mut self.background_thread) };
thread.join().expect("Logging thread panicked");
}
}
impl tracing::Subscriber for Subscriber {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.inner.level <= *metadata.level()
}
fn new_span(&self, span: &Attributes<'_>) -> Id {
let id = Id::from_u64(self.inner.current_span_id.fetch_add(1, Ordering::Relaxed));
let mut visitor = RecordVisitor::new();
span.record(&mut visitor);
self.inner
.background_sender
.send(Command::from_data(CommandData::CreateSpan {
id: id.clone(),
metadata: span.metadata(),
data: visitor.into_data(),
}))
.expect("Cannot send span to logger");
let parent = if span.is_contextual() {
CURRENT_SPAN.with(|v| v.borrow().clone())
} else {
span.parent().map(Id::clone)
};
if let Some(parent_id) = parent {
self.add_parent(parent_id, id.clone())
}
id
}
fn record(&self, span: &Id, values: &Record<'_>) {
let mut visitor = RecordVisitor::new();
values.record(&mut visitor);
self.inner
.background_sender
.send(Command::from_data(CommandData::RecordSpanData {
id: span.clone(),
data: visitor.into_data(),
}))
.expect("Cannot send record to logger");
}
fn record_follows_from(&self, span: &Id, follows: &Id) {
self.add_parent(follows.clone(), span.clone())
}
fn event(&self, event: &Event<'_>) {
if *event.metadata().level() > self.inner.level {
return;
}
let mut visitor = RecordVisitor::new();
event.record(&mut visitor);
let span = if event.is_contextual() {
CURRENT_SPAN.with(|v| v.borrow().clone())
} else {
event.parent().map(Id::clone)
};
self.inner
.background_sender
.send(Command::from_data(CommandData::Event {
span_id: span,
metadata: event.metadata(),
data: visitor.into_data(),
}))
.expect("Cannot send event to logger");
}
fn enter(&self, span: &Id) {
CURRENT_SPAN.with(|v| v.replace(Some(span.clone())));
}
fn exit(&self, span: &Id) {
let parent = self
.inner
.span_parents
.lock()
.expect("Need to lock parental_relationship map")
.get(&span.into_u64())
.map(|v| Id::from_u64(*v));
CURRENT_SPAN.with(|v| v.replace(parent));
}
fn try_close(&self, id: Id) -> bool {
self.inner
.span_parents
.lock()
.expect("Need to lock parental_relationship map")
.remove(&id.into_u64());
true
}
}
struct RecordVisitor {
data: HashMap<String, Data>,
}
impl RecordVisitor {
fn new() -> Self {
Self {
data: HashMap::default(),
}
}
#[allow(clippy::missing_const_for_fn)] fn into_data(self) -> HashMap<String, Data> {
self.data
}
}
impl Visit for RecordVisitor {
fn record_i64(&mut self, field: &Field, value: i64) {
self.data.insert(field.name().to_string(), Data::I64(value));
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.data.insert(field.name().to_string(), Data::U64(value));
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.data.insert(field.name().to_string(), Data::Bool(value));
}
fn record_str(&mut self, field: &Field, value: &str) {
self.data
.insert(field.name().to_string(), Data::String(value.to_string()));
}
fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
self.data
.insert(field.name().to_string(), Data::String(format!("{:?}", value)));
}
}