mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-27 19:17:13 +00:00
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.
This commit is contained in:
@@ -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]
|
#[tauri::command]
|
||||||
fn toggle_log_pause(pause: bool) {
|
fn toggle_log_pause(pause: bool) {
|
||||||
@@ -4085,13 +4085,15 @@ pub fn run() {
|
|||||||
})
|
})
|
||||||
.plugin(
|
.plugin(
|
||||||
tauri_plugin_log::Builder::new()
|
tauri_plugin_log::Builder::new()
|
||||||
.filter(|_meta| !LOG_PAUSED.load(std::sync::atomic::Ordering::Relaxed))
|
|
||||||
.targets([
|
.targets([
|
||||||
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
|
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
|
||||||
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }),
|
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 })
|
.level(if cfg!(debug_assertions) { log::LevelFilter::Debug } else { log::LevelFilter::Info })
|
||||||
.filter(|metadata| {
|
.filter(|metadata| {
|
||||||
|
if LOG_PAUSED.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let target = metadata.target();
|
let target = metadata.target();
|
||||||
!target.starts_with("webview::")
|
!target.starts_with("webview::")
|
||||||
&& !target.starts_with("hyper")
|
&& !target.starts_with("hyper")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { invokeCommand as invoke } from '../ipc';
|
import { invokeCommand as invoke } from '../ipc';
|
||||||
import { save } from '@tauri-apps/plugin-dialog';
|
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 { FileDown, Trash2, Terminal, Filter, Play, Pause, Info, Copy } from 'lucide-react';
|
||||||
import { WindowDragRegion } from './WindowDragRegion';
|
import { WindowDragRegion } from './WindowDragRegion';
|
||||||
import { useToast } from '../contexts/ToastContext';
|
import { useToast } from '../contexts/ToastContext';
|
||||||
@@ -28,12 +28,12 @@ export default function LogsView() {
|
|||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
const [lines, currentPauseState] = await Promise.all([
|
await initLogger();
|
||||||
invoke('read_logs', { limit: MAX_LOG_LINES }),
|
const [lines] = await Promise.all([
|
||||||
invoke('is_log_paused')
|
invoke('read_logs', { limit: MAX_LOG_LINES })
|
||||||
]);
|
]);
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
setIsPaused(currentPauseState);
|
setIsPaused(getLogPaused());
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
const initialLogs = lines.map(message => {
|
const initialLogs = lines.map(message => {
|
||||||
const level: LogEntry['level'] = message.includes('[ERROR]') ? 'Error'
|
const level: LogEntry['level'] = message.includes('[ERROR]') ? 'Error'
|
||||||
@@ -182,7 +182,7 @@ export default function LogsView() {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const newState = !isPaused;
|
const newState = !isPaused;
|
||||||
setIsPaused(newState);
|
setIsPaused(newState);
|
||||||
await invoke('toggle_log_pause', { pause: newState }).catch(console.error);
|
await setLogPaused(newState);
|
||||||
}}
|
}}
|
||||||
className={`app-icon-button ${isPaused ? 'text-accent' : ''}`}
|
className={`app-icon-button ${isPaused ? 'text-accent' : ''}`}
|
||||||
title={isPaused ? "Resume logs" : "Pause logs system"}
|
title={isPaused ? "Resume logs" : "Pause logs system"}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import { invoke as tauriInvoke } from '@tauri-apps/api/core';
|
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 { listen as tauriListen, type Event, type EventCallback, type UnlistenFn } from '@tauri-apps/api/event';
|
||||||
import type { DownloadCategory } from './bindings/DownloadCategory';
|
import type { DownloadCategory } from './bindings/DownloadCategory';
|
||||||
import type { DownloadProgressEvent } from './bindings/DownloadProgressEvent';
|
import type { DownloadProgressEvent } from './bindings/DownloadProgressEvent';
|
||||||
|
|||||||
+3
-1
@@ -4,7 +4,9 @@ import "./index.css";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { ErrorBoundary } from "./components/ErrorBoundary";
|
import { ErrorBoundary } from "./components/ErrorBoundary";
|
||||||
import { ToastProvider } from "./contexts/ToastContext";
|
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 => {
|
const serializeConsoleArguments = (values: unknown[]) => values.map(value => {
|
||||||
if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`;
|
if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`;
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ vi.mock('../ipc', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock window.__TAURI_INTERNALS__ and log to prevent errors
|
// Mock window.__TAURI_INTERNALS__ and log to prevent errors
|
||||||
vi.mock('@tauri-apps/plugin-log', () => ({
|
vi.mock('../utils/logger', () => ({
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
|
warn: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { info } from '@tauri-apps/plugin-log';
|
import { info } from '../utils/logger';
|
||||||
import { invokeCommand as invoke } from '../ipc';
|
import { invokeCommand as invoke } from '../ipc';
|
||||||
|
|
||||||
import type { DownloadItem } from '../bindings/DownloadItem';
|
import type { DownloadItem } from '../bindings/DownloadItem';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist, createJSONStorage, StateStorage } from 'zustand/middleware';
|
import { persist, createJSONStorage, StateStorage } from 'zustand/middleware';
|
||||||
import { invokeCommand as invoke } from '../ipc';
|
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 { ActiveView } from '../bindings/ActiveView';
|
||||||
import type { AppFontSize } from '../bindings/AppFontSize';
|
import type { AppFontSize } from '../bindings/AppFontSize';
|
||||||
import type { ListRowDensity } from '../bindings/ListRowDensity';
|
import type { ListRowDensity } from '../bindings/ListRowDensity';
|
||||||
|
|||||||
@@ -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<void> | null = null;
|
||||||
|
|
||||||
|
export const initLogger = () => {
|
||||||
|
if (!initPromise) {
|
||||||
|
initPromise = invoke<boolean>('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;
|
||||||
Reference in New Issue
Block a user