use std::sync::{Arc, Mutex};
use tracing::Level;
use tracing_subscriber::Layer;
#[derive(Clone)]
pub struct MockCaptureLayer {
captured: Arc<Mutex<Vec<CapturedEvent>>>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct CapturedEvent {
pub level: Level,
pub message: String,
}
impl MockCaptureLayer {
pub fn new() -> Self {
Self {
captured: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn get_captured(&self) -> Vec<CapturedEvent> {
self.captured
.lock()
.expect(
"MockCaptureLayer mutex poisoned - a test thread panicked while holding the lock",
)
.clone()
}
pub fn count(&self) -> usize {
self.captured
.lock()
.expect(
"MockCaptureLayer mutex poisoned - a test thread panicked while holding the lock",
)
.len()
}
pub fn clear(&self) {
self.captured
.lock()
.expect(
"MockCaptureLayer mutex poisoned - a test thread panicked while holding the lock",
)
.clear();
}
}
impl Default for MockCaptureLayer {
fn default() -> Self {
Self::new()
}
}
impl<S> Layer<S> for MockCaptureLayer
where
S: tracing::Subscriber,
{
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut visitor = EventVisitor {
message: String::new(),
};
event.record(&mut visitor);
self.captured
.lock()
.expect(
"MockCaptureLayer mutex poisoned - a test thread panicked while holding the lock",
)
.push(CapturedEvent {
level: *event.metadata().level(),
message: visitor.message,
});
}
}
struct EventVisitor {
message: String,
}
impl tracing::field::Visit for EventVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
#[test]
fn test_mock_capture_layer() {
let capture = MockCaptureLayer::new();
let subscriber = tracing_subscriber::registry().with(capture.clone());
tracing::subscriber::with_default(subscriber, || {
info!("test message");
});
assert_eq!(capture.count(), 1);
let events = capture.get_captured();
assert_eq!(events[0].level, Level::INFO);
}
}