use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use tracing::field::{Field, Visit};
use tracing::span::{Attributes, Id, Record};
use tracing::{Event, Level, Metadata, Subscriber};
pub(crate) fn count_drop_warnings(expected_message: &str, f: impl FnOnce()) -> usize {
struct Count {
hits: Arc<AtomicUsize>,
expected: String,
}
struct Msg<'a> {
expected: &'a str,
matched: bool,
}
impl Visit for Msg<'_> {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
let s = format!("{value:?}");
if s.contains(self.expected) {
self.matched = true;
}
}
}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "message" && value.contains(self.expected) {
self.matched = true;
}
}
}
impl Subscriber for Count {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
*metadata.level() == Level::WARN
}
fn new_span(&self, _span: &Attributes<'_>) -> Id {
Id::from_u64(1)
}
fn record(&self, _span: &Id, _values: &Record<'_>) {}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {}
fn event(&self, event: &Event<'_>) {
let mut msg = Msg {
expected: &self.expected,
matched: false,
};
event.record(&mut msg);
if msg.matched {
self.hits.fetch_add(1, AtomicOrdering::SeqCst);
}
}
fn enter(&self, _span: &Id) {}
fn exit(&self, _span: &Id) {}
}
let hits = Arc::new(AtomicUsize::new(0));
let expected = expected_message.to_owned();
tracing::subscriber::with_default(
Count {
hits: hits.clone(),
expected,
},
f,
);
hits.load(AtomicOrdering::SeqCst)
}