feat: implement Winston logging service
- Add LogEntry interface for structured log data - Create LoggerService with Winston integration - Support for console and file transports (app.log, error.log) - Implement UI log notification callbacks - Provide info, warn, error, and debug logging methods - Configure log rotation (10MB max, 5 files retained) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
6
src/main/models/logger.types.ts
Normal file
6
src/main/models/logger.types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface LogEntry {
|
||||
timestamp: string;
|
||||
level: string;
|
||||
message: string;
|
||||
details?: any;
|
||||
}
|
||||
94
src/main/services/logger.service.ts
Normal file
94
src/main/services/logger.service.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import winston from 'winston';
|
||||
import path from 'path';
|
||||
import { app } from 'electron';
|
||||
import { LogEntry } from '../models/logger.types';
|
||||
|
||||
export class LoggerService {
|
||||
private static instance: winston.Logger | null = null;
|
||||
private static uiLogCallbacks: Set<(logEntry: LogEntry) => void> = new Set();
|
||||
|
||||
static initialize(): winston.Logger {
|
||||
if (this.instance) {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
const logDir = path.join(app.getPath('userData'), 'logs');
|
||||
|
||||
this.instance = winston.createLogger({
|
||||
level: 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.printf(({ level, message, timestamp, stack }) => {
|
||||
if (stack) {
|
||||
return `[${timestamp}] [${level.toUpperCase()}] ${message}\n${stack}`;
|
||||
}
|
||||
return `[${timestamp}] [${level.toUpperCase()}] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
),
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: path.join(logDir, 'app.log'),
|
||||
maxsize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: path.join(logDir, 'error.log'),
|
||||
level: 'error',
|
||||
maxsize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
static onUILog(callback: (logEntry: LogEntry) => void): () => void {
|
||||
this.uiLogCallbacks.add(callback);
|
||||
return () => {
|
||||
this.uiLogCallbacks.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
private static notifyUI(level: string, message: string, details?: any): void {
|
||||
const logEntry: LogEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
level,
|
||||
message,
|
||||
details,
|
||||
};
|
||||
|
||||
this.uiLogCallbacks.forEach((callback) => callback(logEntry));
|
||||
}
|
||||
|
||||
static info(message: string, details?: any): void {
|
||||
if (!this.instance) this.initialize();
|
||||
this.instance!.info(message, details);
|
||||
this.notifyUI('info', message, details);
|
||||
}
|
||||
|
||||
static warn(message: string, details?: any): void {
|
||||
if (!this.instance) this.initialize();
|
||||
this.instance!.warning(message, details);
|
||||
this.notifyUI('warn', message, details);
|
||||
}
|
||||
|
||||
static error(message: string, details?: any): void {
|
||||
if (!this.instance) this.initialize();
|
||||
this.instance!.error(message, details);
|
||||
this.notifyUI('error', message, details);
|
||||
}
|
||||
|
||||
static debug(message: string, details?: any): void {
|
||||
if (!this.instance) this.initialize();
|
||||
this.instance!.debug(message, details);
|
||||
this.notifyUI('debug', message, details);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user