generated from nhcarrigan/template
5b8ae63de1
Added a comprehensive debug console feature that captures and displays logs from both the frontend and backend in a unified interface. Frontend changes: - Created DebugConsole component with real-time log display - Added debugConsoleStore for state management and console capture - Integrated console into main layout - Added toggle button in StatusBar with console icon - Implemented Ctrl+` keyboard shortcut to open/close console - Features: log filtering by level, auto-scroll, timestamps, colour-coding Backend changes: - Added tracing and tracing-subscriber dependencies - Created custom TauriLogLayer to emit Rust logs to frontend - Integrated tracing subscriber in lib.rs setup - Logs are forwarded via Tauri events (debug:log) Key features: - Circular buffer (max 1000 logs) prevents memory issues - Frontend logs captured via console method overrides - Backend logs forwarded from Rust tracing layer - Log level filtering (debug, info, warn, error, all) - Source badges distinguish frontend vs backend logs - Colour-coded log levels for easy identification - Auto-scroll toggle for inspecting older logs - Clear logs button for resetting the console - Beautiful dark-themed UI matching app aesthetic Closes #126
79 lines
1.9 KiB
Rust
79 lines
1.9 KiB
Rust
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<AppHandle>,
|
|
}
|
|
|
|
impl TauriLogLayer {
|
|
pub fn new(app: AppHandle) -> Self {
|
|
Self {
|
|
app: Arc::new(app),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S> Layer<S> 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);
|
|
}
|
|
}
|