use serde::{Deserialize, Serialize}; use std::sync::Arc; use tauri::{AppHandle, Emitter}; use tracing::{Level, Subscriber}; use tracing_subscriber::layer::{Context, Layer}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DebugLogEvent { pub level: String, pub message: String, } #[derive(Clone)] pub struct TauriLogLayer { app: Arc, } impl TauriLogLayer { pub fn new(app: AppHandle) -> Self { Self { app: Arc::new(app), } } } impl Layer for TauriLogLayer where S: Subscriber, { fn on_event( &self, event: &tracing::Event<'_>, _ctx: Context<'_, S>, ) { let metadata = event.metadata(); let level = match *metadata.level() { Level::ERROR => "error", Level::WARN => "warn", Level::INFO => "info", Level::DEBUG => "debug", Level::TRACE => "debug", }; // Extract message from the event 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); } } } let mut visitor = MessageVisitor { message: String::new(), }; event.record(&mut visitor); // If we couldn't extract a message, try to format the whole event if visitor.message.is_empty() { visitor.message = metadata.name().to_string(); } // Strip quotes from the message let message = visitor.message.trim_matches('"').to_string(); let log_event = DebugLogEvent { level: level.to_string(), message, }; // Emit to frontend let _ = self.app.emit("debug:log", log_event); } } #[cfg(test)] mod tests { use super::*; #[test] fn test_debug_log_event_creation() { let event = DebugLogEvent { level: "info".to_string(), message: "Test message".to_string(), }; assert_eq!(event.level, "info"); assert_eq!(event.message, "Test message"); } #[test] fn test_debug_log_event_serialization() { let event = DebugLogEvent { level: "error".to_string(), message: "Error occurred".to_string(), }; let json = serde_json::to_string(&event).unwrap(); assert!(json.contains("\"level\":\"error\"")); assert!(json.contains("\"message\":\"Error occurred\"")); } #[test] fn test_debug_log_event_deserialization() { let json = r#"{"level":"warn","message":"Warning message"}"#; let event: DebugLogEvent = serde_json::from_str(json).unwrap(); assert_eq!(event.level, "warn"); assert_eq!(event.message, "Warning message"); } #[test] fn test_debug_log_event_with_special_characters() { let event = DebugLogEvent { level: "info".to_string(), message: "Message with \"quotes\" and \n newlines".to_string(), }; let json = serde_json::to_string(&event).unwrap(); let decoded: DebugLogEvent = serde_json::from_str(&json).unwrap(); assert_eq!(decoded.level, event.level); assert_eq!(decoded.message, event.message); } #[test] fn test_debug_log_event_with_unicode() { let event = DebugLogEvent { level: "debug".to_string(), message: "Unicode: 日本語 🎉".to_string(), }; let json = serde_json::to_string(&event).unwrap(); let decoded: DebugLogEvent = serde_json::from_str(&json).unwrap(); assert_eq!(decoded.message, "Unicode: 日本語 🎉"); } #[test] fn test_debug_log_event_all_levels() { let levels = vec!["error", "warn", "info", "debug", "trace"]; for level in levels { let event = DebugLogEvent { level: level.to_string(), message: format!("{} level message", level), }; assert_eq!(event.level, level); assert!(event.message.contains(level)); } } }