refactor(logger): optimize logging architecture with 6 improvements

1. Extract shared module: consolidate getLogDir() and isProduction()
   into shared.ts, eliminate duplication across logger modules
2. Make retention config effective: delay file transport creation
   until config is loaded, apply appRetention/auditRetention from config.yaml
3. Add before-quit log flush: close logger and audit logger on
   app exit to prevent log loss
4. Unify logError entry point: remove duplicate logError from index.ts,
   re-export from error-utils.ts with richer error context
5. Renderer log level filtering: add client-side level check in preload
   to skip IPC for filtered-out messages
6. Child logger cache + audit cleanup: cache child loggers in IPC
   handler for performance, remove redundant timestamp format in audit logger

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-04-03 20:53:53 +08:00
parent 020bbcdccc
commit 883f98065a
22 changed files with 300 additions and 176 deletions

View File

@@ -10,7 +10,9 @@
*/
import { ipcMain } from 'electron'
import winston from 'winston'
import { createLogger } from '../services/logger'
import logger from '../services/logger'
import { IPC_CHANNELS, type LogLevel } from '../../shared/ipc-channels'
const log = createLogger('LoggerHandler')
@@ -41,6 +43,7 @@ class LoggerHandlerState {
private buffer: LogEntry[] = []
private debounceTimer: NodeJS.Timeout | null = null
private discardedCount = 0
private childLoggerCache = new Map<string, winston.Logger>()
/**
* Add log entry to buffer
@@ -131,16 +134,27 @@ class LoggerHandlerState {
}
}
/**
* Get or create a cached child logger for a component
* Avoids creating a new child logger for every log entry
* @param component - Component name for the child logger
*/
private getChildLogger(component: string): winston.Logger {
let child = this.childLoggerCache.get(component)
if (!child) {
child = log.child({ source: 'renderer', component })
this.childLoggerCache.set(component, child)
}
return child
}
/**
* Forward a single log entry to Winston logger
* @param entry - Log entry to forward
*/
private forwardToWinston(entry: LogEntry): void {
const context = (entry.context?.component as string) || 'renderer'
const childLogger = log.child({
source: 'renderer',
component: context
})
const childLogger = this.getChildLogger(context)
const message = entry.context?.message
? `[${entry.context.message}] ${entry.message}`
@@ -187,6 +201,7 @@ class LoggerHandlerState {
}
this.buffer = []
this.discardedCount = 0
this.childLoggerCache.clear()
}
}
@@ -197,6 +212,11 @@ const state = new LoggerHandlerState()
* Register IPC handlers for logger
*/
export function registerLoggerHandlers(): void {
// Return current log level to preload for client-side filtering
ipcMain.handle(IPC_CHANNELS.LOGGER_GET_LEVEL, () => {
return logger.level as LogLevel
})
// Use ipcMain.on with send() - fire-and-forget, non-blocking
ipcMain.on(IPC_CHANNELS.LOGGER_FORWARD, (_event, entry: LogEntry) => {
// Validate entry