diff --git a/src/main/ipc/logger-handler.ts b/src/main/ipc/logger-handler.ts index 932d526..74f3b74 100644 --- a/src/main/ipc/logger-handler.ts +++ b/src/main/ipc/logger-handler.ts @@ -9,7 +9,7 @@ * - Error-level logs bypass circuit breaker */ -import { ipcMain } from 'electron' +import { ipcMain, BrowserWindow } from 'electron' import winston from 'winston' import { createLogger } from '../services/logger' import logger from '../services/logger' @@ -161,6 +161,9 @@ class LoggerHandlerState { : entry.message switch (entry.level) { + case 'verbose': + childLogger.verbose(message, entry.context) + break case 'debug': childLogger.debug(message, entry.context) break diff --git a/src/main/services/logger/index.ts b/src/main/services/logger/index.ts index 188c9c4..49e115d 100644 --- a/src/main/services/logger/index.ts +++ b/src/main/services/logger/index.ts @@ -11,8 +11,10 @@ import winston from 'winston' import DailyRotateFile from 'winston-daily-rotate-file' import path from 'path' +import { BrowserWindow } from 'electron' import { serializeError, sanitizeError } from './error-utils' import { getLogDir, isProduction } from './shared' +import { IPC_CHANNELS } from '../../../shared/ipc-channels' // Cache isProduction() at module load — app.isPackaged never changes at runtime const IS_PROD = isProduction() @@ -113,11 +115,18 @@ const logger = winston.createLogger({ }) /** - * Update the logger level dynamically + * Update the logger level dynamically and notify renderer processes * @param level - The new log level */ export function setLogLevel(level: string): void { logger.level = level + + // Broadcast level change to all renderer windows so they update their cached level + for (const win of BrowserWindow.getAllWindows()) { + if (!win.isDestroyed()) { + win.webContents.send(IPC_CHANNELS.LOGGER_LEVEL_CHANGED, level) + } + } } /** diff --git a/src/preload/api/logger.ts b/src/preload/api/logger.ts index 069824c..b214699 100644 --- a/src/preload/api/logger.ts +++ b/src/preload/api/logger.ts @@ -20,6 +20,11 @@ function shouldLog(level: LogLevel): boolean { return (priorities[level] ?? 0) >= (priorities[cachedLevel] ?? 2) } +// Listener for level change broadcasts from main process +function onLevelChanged(_event: Electron.IpcRendererEvent, level: LogLevel): void { + cachedLevel = level +} + export const loggerApi = { log: (level: LogLevel, message: string, context?: Record): void => { // Drop messages below the configured log level @@ -34,10 +39,19 @@ export const loggerApi = { }, /** - * Fetch the current log level from main process and cache it - * Should be called early in renderer initialization + * Fetch the current log level from main process and cache it. + * Also registers a listener for future level changes. + * Should be called early in renderer initialization. */ fetchLevel: async (): Promise => { cachedLevel = (await ipcRenderer.invoke(IPC_CHANNELS.LOGGER_GET_LEVEL)) as LogLevel + ipcRenderer.on(IPC_CHANNELS.LOGGER_LEVEL_CHANGED, onLevelChanged) + }, + + /** + * Remove the level change listener (call on cleanup/unmount) + */ + cleanup: (): void => { + ipcRenderer.removeListener(IPC_CHANNELS.LOGGER_LEVEL_CHANGED, onLevelChanged) } } as const diff --git a/src/shared/ipc-channels.ts b/src/shared/ipc-channels.ts index da3c707..dece290 100644 --- a/src/shared/ipc-channels.ts +++ b/src/shared/ipc-channels.ts @@ -93,6 +93,7 @@ export const IPC_CHANNELS = { // Logger LOGGER_FORWARD: 'logger:forward', LOGGER_GET_LEVEL: 'logger:getLevel', + LOGGER_LEVEL_CHANGED: 'logger:levelChanged', // Report REPORT_LIST_ALL: 'report:listAll',