use logfusion::{debug, error, info, info_span, trace, warn};
use std::sync::{Arc, Mutex};
use tracing::{Level, instrument};
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
#[derive(Debug, Clone)]
struct CapturedEvent {
level: String,
target: String,
message: String,
fields: std::collections::HashMap<String, String>,
}
struct TestCollector {
events: Arc<Mutex<Vec<CapturedEvent>>>,
}
impl TestCollector {
fn new() -> (Self, Arc<Mutex<Vec<CapturedEvent>>>) {
let events = Arc::new(Mutex::new(Vec::new()));
(
Self {
events: events.clone(),
},
events,
)
}
}
impl<S> tracing_subscriber::Layer<S> for TestCollector
where
S: tracing::Subscriber + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
{
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut visitor = FieldVisitor::new();
event.record(&mut visitor);
let captured = CapturedEvent {
level: format!("{}", event.metadata().level()),
target: event.metadata().target().to_string(),
message: visitor.message,
fields: visitor.fields,
};
self.events.lock().unwrap().push(captured);
}
}
struct FieldVisitor {
message: String,
fields: std::collections::HashMap<String, String>,
}
impl FieldVisitor {
fn new() -> Self {
Self {
message: String::new(),
fields: std::collections::HashMap::new(),
}
}
}
impl tracing::field::Visit for FieldVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.message = format!("{:?}", value).trim_matches('"').to_string();
} else {
self.fields
.insert(field.name().to_string(), format!("{:?}", value));
}
}
}
#[test]
fn tracing_basic_functionality() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
error!("Test error message");
warn!("Test warning message");
info!("Test info message");
debug!("Test debug message");
trace!("Test trace message");
let captured = events.lock().unwrap();
assert!(!captured.is_empty());
let error_event = captured.iter().find(|e| e.level == "ERROR");
assert!(error_event.is_some());
assert_eq!(error_event.unwrap().message, "Test error message");
}
#[test]
fn tracing_targeted_logging() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
error!(target: "app::database", "Database connection failed");
warn!(target: "app::network", "Network timeout");
info!(target: "app::auth", "User authenticated");
let captured = events.lock().unwrap();
let db_event = captured.iter().find(|e| e.target == "app::database");
let net_event = captured.iter().find(|e| e.target == "app::network");
let auth_event = captured.iter().find(|e| e.target == "app::auth");
assert!(db_event.is_some());
assert_eq!(db_event.unwrap().message, "Database connection failed");
assert!(net_event.is_some());
assert_eq!(net_event.unwrap().message, "Network timeout");
assert!(auth_event.is_some());
assert_eq!(auth_event.unwrap().message, "User authenticated");
}
#[test]
fn log_crate_bridge_compatibility() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry()
.with(collector)
.with(tracing_subscriber::fmt::layer())
.set_default();
events.lock().unwrap().clear();
log::error!("Log crate error");
log::warn!("Log crate warning");
log::info!("Log crate info");
error!("LogFusion error");
warn!("LogFusion warning");
info!("LogFusion info");
let captured = events.lock().unwrap();
assert!(captured.len() >= 4);
let logfusion_error = captured.iter().find(|e| e.message == "LogFusion error");
assert!(logfusion_error.is_some());
assert_eq!(logfusion_error.unwrap().level, "ERROR");
}
#[test]
fn tracing_structured_logging() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
tracing::info!(
user_id = 12345,
action = "login",
ip_address = "192.168.1.1",
"User login successful"
);
info!("Regular LogFusion log");
let captured = events.lock().unwrap();
let structured_event = captured
.iter()
.find(|e| e.message == "User login successful");
let regular_event = captured
.iter()
.find(|e| e.message == "Regular LogFusion log");
assert!(structured_event.is_some());
assert!(regular_event.is_some());
let structured = structured_event.unwrap();
assert_eq!(structured.level, "INFO");
assert!(structured.fields.contains_key("user_id"));
assert_eq!(structured.fields.get("user_id"), Some(&"12345".to_string()));
assert!(structured.fields.contains_key("action"));
assert_eq!(
structured.fields.get("action"),
Some(&"\"login\"".to_string())
);
assert!(structured.fields.contains_key("ip_address"));
assert_eq!(
structured.fields.get("ip_address"),
Some(&"\"192.168.1.1\"".to_string())
);
}
#[test]
fn tracing_filtering() {
let (collector, events) = TestCollector::new();
let filter = EnvFilter::new("warn");
let _guard = tracing_subscriber::registry()
.with(collector)
.with(filter)
.set_default();
events.lock().unwrap().clear();
error!("This should appear");
warn!("This should also appear");
info!("This should be filtered out");
debug!("This should be filtered out");
let captured = events.lock().unwrap();
let error_count = captured.iter().filter(|e| e.level == "ERROR").count();
let warn_count = captured.iter().filter(|e| e.level == "WARN").count();
let info_count = captured.iter().filter(|e| e.level == "INFO").count();
let debug_count = captured.iter().filter(|e| e.level == "DEBUG").count();
assert!(error_count > 0);
assert!(warn_count > 0);
assert_eq!(info_count, 0);
assert_eq!(debug_count, 0);
}
#[test]
fn tracing_subscriber_layers() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry()
.with(collector)
.with(tracing_subscriber::fmt::layer().with_target(false))
.set_default();
events.lock().unwrap().clear();
error!("Multi-layer error");
info!("Multi-layer info");
let captured = events.lock().unwrap();
assert!(!captured.is_empty());
let error_event = captured.iter().find(|e| e.message == "Multi-layer error");
assert!(error_event.is_some());
assert_eq!(error_event.unwrap().level, "ERROR");
}
#[test]
fn tracing_spans_integration() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
let span = tracing::span!(Level::INFO, "test_operation", user_id = 12345);
let _enter = span.enter();
info!("Operation started");
error!("Operation failed");
drop(_enter);
drop(span);
let captured = events.lock().unwrap();
assert!(!captured.is_empty());
let info_event = captured.iter().find(|e| e.level == "INFO");
assert!(info_event.is_some());
assert_eq!(info_event.unwrap().message, "Operation started");
}
#[test]
fn tracing_spans_comprehensive() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
let span = tracing::span!(
Level::INFO,
"user_operation",
user_id = 12345,
operation = "profile_update",
session_id = "sess_abc123"
);
let _enter = span.enter();
info!("Inside span operation");
debug!("Debug message in span");
drop(_enter);
let outer_span = tracing::span!(Level::INFO, "outer", request_id = "req_456");
let _outer_enter = outer_span.enter();
let inner_span = tracing::span!(Level::DEBUG, "inner", operation = "validation");
let _inner_enter = inner_span.enter();
warn!("Warning in nested span");
drop(_inner_enter);
drop(_outer_enter);
let logfusion_span = info_span!("logfusion_span", component = "auth");
let _logfusion_enter = logfusion_span.enter();
error!("Error in LogFusion span");
drop(_logfusion_enter);
let captured = events.lock().unwrap();
assert!(!captured.is_empty());
let span_info = captured
.iter()
.find(|e| e.message == "Inside span operation");
assert!(span_info.is_some());
assert_eq!(span_info.unwrap().level, "INFO");
let nested_warn = captured
.iter()
.find(|e| e.message == "Warning in nested span");
assert!(nested_warn.is_some());
assert_eq!(nested_warn.unwrap().level, "WARN");
let logfusion_error = captured
.iter()
.find(|e| e.message == "Error in LogFusion span");
assert!(logfusion_error.is_some());
assert_eq!(logfusion_error.unwrap().level, "ERROR");
}
#[test]
fn tracing_instrumentation() {
let (collector, events) = TestCollector::new();
let _guard = tracing_subscriber::registry().with(collector).set_default();
events.lock().unwrap().clear();
#[instrument(level = "info")]
fn instrumented_function(user_id: u64, action: &str) {
info!("Processing action: {}", action);
if action == "fail" {
error!("Action failed for user {}", user_id);
} else {
info!("Action completed successfully");
}
}
instrumented_function(12345, "process");
instrumented_function(67890, "fail");
let captured = events.lock().unwrap();
let process_events: Vec<_> = captured
.iter()
.filter(|e| {
e.message.contains("Processing action") || e.message.contains("completed successfully")
})
.collect();
let error_events: Vec<_> = captured
.iter()
.filter(|e| e.message.contains("Action failed"))
.collect();
assert!(!process_events.is_empty());
assert!(!error_events.is_empty());
}