#![feature(unboxed_closures)]
#![feature(fn_traits)]
#![doc = include_str!("../Readme.md")]
use tracing::{Id, Level, Event, span, Metadata, Subscriber};
use tracing::subscriber::Interest;
use tracing::field::{Field, Visit};
use std::ops::RangeInclusive;
use std::fmt::{Display, Write};
use std::sync::atomic::AtomicUsize;
use core::*;
#[cfg(feature = "print_colored")]
use owo_colors::{AnsiColors, OwoColorize, Stream};
mod core;
#[cfg(feature = "print_colored")]
const SPAN_COLORS: [AnsiColors; 6] = [
AnsiColors::BrightBlue,
AnsiColors::BrightCyan,
AnsiColors::BrightMagenta,
AnsiColors::Yellow,
AnsiColors::BrightGreen,
AnsiColors::Red,
];
#[cfg(not(feature = "print_colored"))]
#[derive(Copy, Clone)]
struct AnsiColors;
fn colorize(color: Option<AnsiColors>, message: String) -> String {
match color {
#[cfg(feature = "print_colored")]
Some(index) => {
message
.if_supports_color(Stream::Stdout, |text| text.color(index))
.to_string()
},
#[cfg(not(feature = "print_colored"))]
Some(_) => unreachable!(),
None => message
}
}
struct PrintToStdout;
impl FnOnce<(String,)> for PrintToStdout {
type Output = ();
extern "rust-call" fn call_once(self, args: (String,)) {
self.call(args)
}
}
impl<'a> FnMut<(String,)> for PrintToStdout {
extern "rust-call" fn call_mut(&mut self, args: (String,)) {
self.call(args)
}
}
impl<'a> Fn<(String,)> for PrintToStdout {
extern "rust-call" fn call(&self, args: (String,)) {
print!("{}", args.0);
std::io::Write::flush(&mut std::io::stdout()).unwrap()
}
}
struct SpanData {
desc: FieldRecorder,
depth: usize,
metadata: &'static Metadata<'static>,
color: Option<AnsiColors>,
#[allow(unused)]
color_counter: Option<AtomicUsize>
}
impl SpanData {
#[cfg(feature = "print_colored")]
fn color_for_child(&self) -> Option<AnsiColors> {
if let Some(color_counter) = &self.color_counter {
debug_assert_eq!(0, self.depth);
Some(SPAN_COLORS[color_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) % SPAN_COLORS.len()])
} else {
self.color
}
}
#[cfg(not(feature = "print_colored"))]
fn color_for_child(&self) -> Option<AnsiColors> {
None
}
}
pub struct DelayedLogger {
base: DelayedLoggerImpl<SpanData, PrintToStdout>,
levels: RangeInclusive<Level>,
max_depth: usize
}
impl DelayedLogger {
pub fn new(max_depth: usize, silent_period: u64, levels: RangeInclusive<Level>) -> Self {
Self {
base: DelayedLoggerImpl::new(silent_period, PrintToStdout),
levels: levels,
max_depth: max_depth
}
}
pub fn init(max_depth: usize, silent_period: u64, levels: RangeInclusive<Level>) {
tracing::subscriber::set_global_default(Self::new(max_depth, silent_period, levels)).unwrap()
}
pub fn init_test() {
_ = tracing::subscriber::set_global_default(Self::new(4, 100000, Level::ERROR..=Level::TRACE));
}
}
#[derive(Clone)]
struct FieldRecorder {
message: Option<String>,
fields: Option<String>
}
impl FieldRecorder {
fn new() -> Self {
Self { message: None, fields: None }
}
fn to_string(self) -> String {
match (self.message, self.fields) {
(Some(mut message), Some(fields)) => {
write!(&mut message, "({})", fields).unwrap();
message
},
(Some(mut message), None) => {
write!(&mut message, ".").unwrap();
message
},
(None, Some(fields)) => {
format!("({})", fields)
},
(None, None) => ".".to_owned()
}
}
}
impl Display for FieldRecorder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(message) = &self.message {
write!(f, "{}", message)?;
}
if let Some(fields) = &self.fields {
write!(f, "({})", fields)
} else {
write!(f, ".")
}
}
}
impl Visit for FieldRecorder {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = Some(format!("{:?}", value));
} else {
if let Some(fields) = &mut self.fields {
write!(fields, ", {}={:?}", field.name(), value).unwrap();
} else {
self.fields = Some(format!("{}={:?}", field.name(), value));
}
}
}
}
impl Subscriber for DelayedLogger {
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
if self.levels.contains(metadata.level()) {
Interest::always()
} else {
Interest::never()
}
}
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.levels.contains(metadata.level())
}
fn current_span(&self) -> tracing_core::span::Current {
let mut result = tracing_core::span::Current::none();
if let Some(span) = self.base.span_stack().borrow().last().copied() {
self.base.span_data(span, |data, _, _| result =
tracing_core::span::Current::new(Id::from_non_zero_u64(span), data.metadata)
);
}
return result;
}
fn new_span(&self, span: &span::Attributes<'_>) -> Id {
let mut fields = FieldRecorder::new();
span.record(&mut fields);
fields.message = Some(span.metadata().name().to_owned());
let id = if let Some(parent_id) = span.parent() {
let mut depth = 0;
let mut color = None;
self.base.span_data(parent_id.into_non_zero_u64(), |data, _, _| {
depth = data.depth + 1;
color = data.color_for_child();
});
self.base.create_span_with_parent(SpanData {
color_counter: if depth == 0 { Some(AtomicUsize::new(0)) } else { None },
desc: fields,
metadata: span.metadata(),
depth: depth,
color: color
}, Some(parent_id.into_non_zero_u64()))
} else {
self.base.create_span(|parent| {
let (depth, color) = if let Some((parent_data, _)) = parent {
(parent_data.depth + 1, parent_data.color_for_child())
} else {
(0, None)
};
SpanData {
color_counter: if depth == 0 { Some(AtomicUsize::new(0)) } else { None },
desc: fields,
metadata: span.metadata(),
depth: depth,
color: color
}
})
};
return Id::from_non_zero_u64(id);
}
fn record(&self, _span: &Id, _values: &span::Record<'_>) {
unimplemented!()
}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {
}
fn event(&self, event: &Event<'_>) {
if self.levels.contains(event.metadata().level()) {
let mut is_within_depth = false;
let mut color = None;
if let Some(span) = self.base.span_stack().borrow().last().copied() {
self.base.span_data(span, |data, _, _| if data.depth < self.max_depth {
is_within_depth = true;
color = data.color;
});
if is_within_depth {
let mut fields = FieldRecorder::new();
event.record(&mut fields);
self.base.send_message(colorize(color, fields.to_string()), span);
}
} else {
let mut fields = FieldRecorder::new();
event.record(&mut fields);
PrintToStdout(fields.to_string())
}
}
}
fn enter(&self, span: &Id) {
self.base.enter(span.into_non_zero_u64());
let mut message = None;
self.base.span_data(span.into_non_zero_u64(), |data, _, _| if data.depth < self.max_depth {
message = Some(colorize(data.color, data.desc.clone().to_string()));
} else if data.depth == self.max_depth {
message = Some(colorize(data.color, format!("{}...", data.desc)));
});
if let Some(message) = message {
self.base.send_message(message, span.into_non_zero_u64());
}
}
fn exit(&self, span: &Id) {
let mut message = None;
self.base.span_data(span.into_non_zero_u64(), |data, _, running_time| if data.depth == 0 {
message = Some(format!("done({} us)\n", running_time.as_micros()));
} else if data.depth <= self.max_depth {
message = Some(colorize(data.color, format!("done({} us)", running_time.as_micros())));
});
if let Some(message) = message {
self.base.send_message(message, span.into_non_zero_u64());
}
self.base.exit(span.into_non_zero_u64())
}
fn clone_span(&self, id: &Id) -> Id {
self.base.clone_span(id.into_non_zero_u64());
return id.clone();
}
fn try_close(&self, id: Id) -> bool {
self.base.delete_span(id.into_non_zero_u64())
}
}