From eae3cf6180143ed382061d191af5d49b687b8a32 Mon Sep 17 00:00:00 2001 From: NimBold Date: Tue, 23 Jun 2026 04:33:24 +0330 Subject: [PATCH] feat(logging): default to paused state and prevent performance hit via frontend wrapper - Default LOG_PAUSED to true in backend. - Combine tauri_plugin_log filters to drop backend logs when paused. - Create frontend logger wrapper to intercept and drop logs before IPC when paused. - Sync LogsView state directly with logger wrapper and handle initialization races. --- src-tauri/src/lib.rs | 6 +++-- src/components/LogsView.tsx | 12 ++++----- src/ipc.ts | 2 +- src/main.tsx | 4 ++- src/store/useDownloadStore.test.ts | 3 ++- src/store/useDownloadStore.ts | 2 +- src/store/useSettingsStore.ts | 2 +- src/utils/logger.ts | 40 ++++++++++++++++++++++++++++++ 8 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 src/utils/logger.ts diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bccc0d7..9a311d6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3730,7 +3730,7 @@ mod tests { } } -static LOG_PAUSED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); +static LOG_PAUSED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true); #[tauri::command] fn toggle_log_pause(pause: bool) { @@ -4085,13 +4085,15 @@ pub fn run() { }) .plugin( tauri_plugin_log::Builder::new() - .filter(|_meta| !LOG_PAUSED.load(std::sync::atomic::Ordering::Relaxed)) .targets([ tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout), tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }), ]) .level(if cfg!(debug_assertions) { log::LevelFilter::Debug } else { log::LevelFilter::Info }) .filter(|metadata| { + if LOG_PAUSED.load(std::sync::atomic::Ordering::Relaxed) { + return false; + } let target = metadata.target(); !target.starts_with("webview::") && !target.starts_with("hyper") diff --git a/src/components/LogsView.tsx b/src/components/LogsView.tsx index 1a7c2cc..be531e5 100644 --- a/src/components/LogsView.tsx +++ b/src/components/LogsView.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { invokeCommand as invoke } from '../ipc'; import { save } from '@tauri-apps/plugin-dialog'; -import { attachLogger } from '@tauri-apps/plugin-log'; +import { attachLogger, setLogPaused, getLogPaused, initLogger } from '../utils/logger'; import { FileDown, Trash2, Terminal, Filter, Play, Pause, Info, Copy } from 'lucide-react'; import { WindowDragRegion } from './WindowDragRegion'; import { useToast } from '../contexts/ToastContext'; @@ -28,12 +28,12 @@ export default function LogsView() { const init = async () => { try { - const [lines, currentPauseState] = await Promise.all([ - invoke('read_logs', { limit: MAX_LOG_LINES }), - invoke('is_log_paused') + await initLogger(); + const [lines] = await Promise.all([ + invoke('read_logs', { limit: MAX_LOG_LINES }) ]); if (!active) return; - setIsPaused(currentPauseState); + setIsPaused(getLogPaused()); if (!active) return; const initialLogs = lines.map(message => { const level: LogEntry['level'] = message.includes('[ERROR]') ? 'Error' @@ -182,7 +182,7 @@ export default function LogsView() { onClick={async () => { const newState = !isPaused; setIsPaused(newState); - await invoke('toggle_log_pause', { pause: newState }).catch(console.error); + await setLogPaused(newState); }} className={`app-icon-button ${isPaused ? 'text-accent' : ''}`} title={isPaused ? "Resume logs" : "Pause logs system"} diff --git a/src/ipc.ts b/src/ipc.ts index a3f5dc2..2dc973a 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -1,5 +1,5 @@ import { invoke as tauriInvoke } from '@tauri-apps/api/core'; -import { error as logError } from '@tauri-apps/plugin-log'; +import { error as logError } from './utils/logger'; import { listen as tauriListen, type Event, type EventCallback, type UnlistenFn } from '@tauri-apps/api/event'; import type { DownloadCategory } from './bindings/DownloadCategory'; import type { DownloadProgressEvent } from './bindings/DownloadProgressEvent'; diff --git a/src/main.tsx b/src/main.tsx index 8e128c7..8332baf 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,7 +4,9 @@ import "./index.css"; import App from "./App"; import { ErrorBoundary } from "./components/ErrorBoundary"; import { ToastProvider } from "./contexts/ToastContext"; -import { error as logError, warn as logWarn } from "@tauri-apps/plugin-log"; +import { error as logError, warn as logWarn, initLogger } from "./utils/logger"; + +void initLogger(); const serializeConsoleArguments = (values: unknown[]) => values.map(value => { if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`; diff --git a/src/store/useDownloadStore.test.ts b/src/store/useDownloadStore.test.ts index d5e0fa0..40d5601 100644 --- a/src/store/useDownloadStore.test.ts +++ b/src/store/useDownloadStore.test.ts @@ -8,8 +8,9 @@ vi.mock('../ipc', () => ({ })); // Mock window.__TAURI_INTERNALS__ and log to prevent errors -vi.mock('@tauri-apps/plugin-log', () => ({ +vi.mock('../utils/logger', () => ({ info: vi.fn(), + warn: vi.fn(), error: vi.fn(), })); diff --git a/src/store/useDownloadStore.ts b/src/store/useDownloadStore.ts index 30c6cb5..656fca2 100644 --- a/src/store/useDownloadStore.ts +++ b/src/store/useDownloadStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { info } from '@tauri-apps/plugin-log'; +import { info } from '../utils/logger'; import { invokeCommand as invoke } from '../ipc'; import type { DownloadItem } from '../bindings/DownloadItem'; diff --git a/src/store/useSettingsStore.ts b/src/store/useSettingsStore.ts index ade0a7a..df352be 100644 --- a/src/store/useSettingsStore.ts +++ b/src/store/useSettingsStore.ts @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { persist, createJSONStorage, StateStorage } from 'zustand/middleware'; import { invokeCommand as invoke } from '../ipc'; -import { info } from '@tauri-apps/plugin-log'; +import { info } from '../utils/logger'; import type { ActiveView } from '../bindings/ActiveView'; import type { AppFontSize } from '../bindings/AppFontSize'; import type { ListRowDensity } from '../bindings/ListRowDensity'; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..ba26afb --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,40 @@ +import { info as tauriInfo, warn as tauriWarn, error as tauriError, debug as tauriDebug, trace as tauriTrace, attachLogger as tauriAttachLogger } from '@tauri-apps/plugin-log'; +import { invoke } from '@tauri-apps/api/core'; + +// Default to true to match backend default +let isPaused = true; + +let initPromise: Promise | null = null; + +export const initLogger = () => { + if (!initPromise) { + initPromise = invoke('is_log_paused') + .then(paused => { isPaused = paused; }) + .catch(e => console.error("Failed to init logger state", e)); + } + return initPromise; +}; + +export const setLogPaused = async (pause: boolean) => { + isPaused = pause; + await invoke('toggle_log_pause', { pause }).catch(console.error); +}; + +export const getLogPaused = () => isPaused; + +export const info = async (message: string) => { + if (!isPaused) return tauriInfo(message); +}; +export const warn = async (message: string) => { + if (!isPaused) return tauriWarn(message); +}; +export const error = async (message: string) => { + if (!isPaused) return tauriError(message); +}; +export const debug = async (message: string) => { + if (!isPaused) return tauriDebug(message); +}; +export const trace = async (message: string) => { + if (!isPaused) return tauriTrace(message); +}; +export const attachLogger = tauriAttachLogger;