1use std::fmt;
2use std::sync::Mutex;
3use tracing::{Event, Subscriber};
4use tracing_subscriber::fmt::format::Writer;
5use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
6use tracing_subscriber::registry::LookupSpan;
7
8#[derive(Clone, Hash, Eq, PartialEq)]
9struct EventKey {
10 message: String,
11 level: tracing::Level,
12 target: String,
13}
14
15pub struct DeduplicatingFormatter<F> {
16 inner: F,
17 state: Mutex<DeduplicationState>,
18}
19
20struct DeduplicationState {
21 last_event: Option<EventKey>,
22 repeat_count: usize,
23}
24
25impl<F> DeduplicatingFormatter<F> {
26 pub fn new(inner: F) -> Self {
27 Self {
28 inner,
29 state: Mutex::new(DeduplicationState {
30 last_event: None,
31 repeat_count: 0,
32 }),
33 }
34 }
35}
36
37impl<S, N, F> FormatEvent<S, N> for DeduplicatingFormatter<F>
38where
39 S: Subscriber + for<'a> LookupSpan<'a>,
40 N: for<'a> FormatFields<'a> + 'static,
41 F: FormatEvent<S, N>,
42{
43 fn format_event(
44 &self,
45 ctx: &FmtContext<'_, S, N>,
46 mut writer: Writer<'_>,
47 event: &Event<'_>,
48 ) -> fmt::Result {
49 let mut visitor = MessageVisitor::default();
50 event.record(&mut visitor);
51
52 let key = EventKey {
53 message: visitor.message,
54 level: *event.metadata().level(),
55 target: event.metadata().target().to_string(),
56 };
57
58 let mut state = self.state.lock().unwrap();
59
60 match &state.last_event {
61 Some(last) if last == &key => {
62 state.repeat_count += 1;
64 return Ok(()); }
66 _ => {
67 if state.repeat_count > 0 {
69 let repeat_count = state.repeat_count + 1;
70 writeln!(writer, "previous message repeated {} times", repeat_count)?;
71 }
72
73 state.last_event = Some(key);
74 state.repeat_count = 0;
75 }
76 }
77
78 drop(state);
79
80 self.inner.format_event(ctx, writer, event)
82 }
83}
84
85#[derive(Default)]
86struct MessageVisitor {
87 message: String,
88}
89
90impl tracing::field::Visit for MessageVisitor {
91 fn record_debug(
92 &mut self,
93 field: &tracing::field::Field,
94 value: &dyn std::fmt::Debug,
95 ) {
96 if field.name() == "message" {
97 self.message = format!("{:?}", value);
98 }
99 }
100
101 fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
102 if field.name() == "message" {
103 self.message = value.to_string();
104 }
105 }
106}