use std::fmt;
use tracing::{Event, field::Visit};
use tracing_subscriber::{
fmt::{
FmtContext,
format::{FormatEvent, FormatFields, Writer},
},
registry::LookupSpan,
};
#[derive(Default)]
pub struct PlainFormatter {
display_level: bool,
display_timestamp: bool,
}
impl PlainFormatter {
pub fn new() -> Self {
Self {
display_level: false,
display_timestamp: false,
}
}
pub fn with_level(mut self, display: bool) -> Self {
self.display_level = display;
self
}
pub fn with_timestamp(mut self, display: bool) -> Self {
self.display_timestamp = display;
self
}
}
impl<S, N> FormatEvent<S, N> for PlainFormatter
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
_ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
let mut visitor = MessageVisitor { message: None };
event.record(&mut visitor);
let message = visitor.message.ok_or(fmt::Error)?;
if self.display_timestamp {
let now = chrono::Local::now();
write!(writer, "{} ", now.format("%Y-%m-%d %H:%M:%S"))?;
}
if self.display_level {
let metadata = event.metadata();
write!(writer, "{:5} ", metadata.level())?;
}
writeln!(writer, "{message}")
}
}
struct MessageVisitor {
message: Option<String>,
}
impl Visit for MessageVisitor {
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
self.message = Some(value.to_string());
}
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
self.message = Some(format!("{value:?}"));
}
}
}