use std::fmt;
use std::sync::Mutex;
use tracing::{Event, Subscriber};
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan;
#[derive(Clone, Hash, Eq, PartialEq)]
struct EventKey {
message: String,
level: tracing::Level,
target: String,
}
pub struct DeduplicatingFormatter<F> {
inner: F,
state: Mutex<DeduplicationState>,
}
struct DeduplicationState {
last_event: Option<EventKey>,
repeat_count: usize,
}
impl<F> DeduplicatingFormatter<F> {
pub fn new(inner: F) -> Self {
Self {
inner,
state: Mutex::new(DeduplicationState {
last_event: None,
repeat_count: 0,
}),
}
}
}
impl<S, N, F> FormatEvent<S, N> for DeduplicatingFormatter<F>
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
F: FormatEvent<S, N>,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
let mut visitor = MessageVisitor::default();
event.record(&mut visitor);
let key = EventKey {
message: visitor.message,
level: *event.metadata().level(),
target: event.metadata().target().to_string(),
};
let mut state = self.state.lock().unwrap();
match &state.last_event {
Some(last) if last == &key => {
state.repeat_count += 1;
return Ok(()); }
_ => {
if state.repeat_count > 0 {
let repeat_count = state.repeat_count + 1;
writeln!(writer, "previous message repeated {} times", repeat_count)?;
}
state.last_event = Some(key);
state.repeat_count = 0;
}
}
drop(state);
self.inner.format_event(ctx, writer, event)
}
}
#[derive(Default)]
struct MessageVisitor {
message: String,
}
impl tracing::field::Visit for MessageVisitor {
fn record_debug(
&mut self,
field: &tracing::field::Field,
value: &dyn std::fmt::Debug,
) {
if field.name() == "message" {
self.message = format!("{:?}", value);
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
self.message = value.to_string();
}
}
}