feat: 重写日志服务

This commit is contained in:
DEV_DSW
2025-12-18 14:48:38 +08:00
parent 8dec7d676e
commit 69a8e9472f
2 changed files with 177 additions and 26 deletions

View File

@@ -1,29 +1,180 @@
import * as log4js from 'log4js';
import { IPC_EVENTS } from '@common/constants';
import { promisify } from 'util';
import { app, ipcMain } from 'electron';
import log from 'electron-log';
import * as path from 'path';
import * as fs from 'fs';
log4js.configure({
appenders: {
out: {
type: 'stdout'
},
app: {
type: 'file',
filename: 'logs/app.log',
backups: 3,
compress: false,
encoding: 'utf-8',
layout: {
type: 'pattern',
pattern: '[%d{yyyy-MM-dd hh:mm:ss.SSS}] [%p] %m'
},
keepFileExt: true
// 转换为Promise形式的fs方法
const readdirAsync = promisify(fs.readdir);
const statAsync = promisify(fs.stat);
const unlinkAsync = promisify(fs.unlink);
class LogService {
private static _instance: LogService;
// 日志保留天数默认7天
private LOG_RETENTION_DAYS = 7;
// 清理间隔默认24小时毫秒
private readonly CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1000;
private constructor() {
const logPath = path.join(app.getPath('userData'), 'logs');
// c:users/{username}/AppData/Roaming/{appName}/logs
// 创建日志目录
try {
if (!fs.existsSync(logPath)) {
fs.mkdirSync(logPath, { recursive: true });
}
} catch (err) {
this.error('Failed to create log directory:', err);
}
},
categories: {
default: {
appenders: ['out', 'app'],
level: 'debug'
// 配置electron-log
log.transports.file.resolvePathFn = () => {
// 使用当前日期作为日志文件名,格式为 YYYY-MM-DD.log
const today = new Date();
const formattedDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
return path.join(logPath, `${formattedDate}.log`);
};
// 配置日志格式
log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}';
// 配置日志文件大小限制默认10MB
log.transports.file.maxSize = 10 * 1024 * 1024; // 10MB
// 配置控制台日志级别开发环境可以设置为debug生产环境可以设置为info
log.transports.console.level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
// 配置文件日志级别
log.transports.file.level = 'debug';
// 设置IPC事件
this._setupIpcEvents();
// 重写console方法
this._rewriteConsole();
this.info('LogService initialized successfully.');
this._cleanupOldLogs();
// 定时清理旧日志
setInterval(() => this._cleanupOldLogs(), this.CLEANUP_INTERVAL_MS);
}
private _setupIpcEvents() {
ipcMain.on(IPC_EVENTS.LOG_DEBUG, (_e, message: string, ...meta: any[]) => this.debug(message, ...meta));
ipcMain.on(IPC_EVENTS.LOG_INFO, (_e, message: string, ...meta: any[]) => this.info(message, ...meta));
ipcMain.on(IPC_EVENTS.LOG_WARN, (_e, message: string, ...meta: any[]) => this.warn(message, ...meta));
ipcMain.on(IPC_EVENTS.LOG_ERROR, (_e, message: string, ...meta: any[]) => this.error(message, ...meta));
}
private _rewriteConsole() {
console.debug = log.debug;
console.log = log.info;
console.info = log.info;
console.warn = log.warn;
console.error = log.error;
}
private async _cleanupOldLogs() {
try {
const logPath = path.join(app.getPath('userData'), 'logs');
if (!fs.existsSync(logPath)) return;
const now = new Date();
const expirationDate = new Date(now.getTime() - this.LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000);
const files = await readdirAsync(logPath);
let deletedCount = 0;
for (const file of files) {
if (!file.endsWith('.log')) continue;
const filePath = path.join(logPath, file);
try {
const stats = await statAsync(filePath);
if (stats.isFile() && (stats.birthtime < expirationDate)) {
await unlinkAsync(filePath);
deletedCount++;
}
} catch (error) {
this.error(`Failed to delete old log file ${filePath}:`, error);
}
}
if (deletedCount > 0) {
this.info(`Successfully cleaned up ${deletedCount} old log files.`);
}
} catch (err) {
this.error('Failed to cleanup old logs:', err);
}
}
});
export const logger = log4js.getLogger();
public static getInstance(): LogService {
if (!this._instance) {
this._instance = new LogService();
}
return this._instance;
}
/**
* 记录调试信息
* @param {string} message - 日志消息
* @param {any[]} meta - 附加的元数据
*/
public debug(message: string, ...meta: any[]): void {
log.debug(message, ...meta);
}
/**
* 记录一般信息
* @param {string} message - 日志消息
* @param {any[]} meta - 附加的元数据
*/
public info(message: string, ...meta: any[]): void {
log.info(message, ...meta);
}
/**
* 记录警告信息
* @param {string} message - 日志消息
* @param {any[]} meta - 附加的元数据
*/
public warn(message: string, ...meta: any[]): void {
log.warn(message, ...meta);
}
/**
* 记录错误信息
* @param {string} message - 日志消息
* @param {any[]} meta - 附加的元数据,通常是错误对象
*/
public error(message: string, ...meta: any[]): void {
log.error(message, ...meta);
}
public logApiRequest(endpoint: string, data: any = {}, method: string = 'POST'): void {
this.info(`API Request: ${endpoint}, Method: ${method}, Request: ${JSON.stringify(data)}`);
}
public logApiResponse(endpoint: string, response: any = {}, statusCode: number = 200, responseTime: number = 0): void {
if (statusCode >= 400) {
this.error(`API Error Response: ${endpoint}, Status: ${statusCode}, Response Time: ${responseTime}ms, Response: ${JSON.stringify(response)}`);
} else {
this.debug(`API Response: ${endpoint}, Status: ${statusCode}, Response Time: ${responseTime}ms, Response: ${JSON.stringify(response)}`);
}
}
public logUserOperation(operation: string, userId: string = 'unknown', details: any = {}): void {
this.info(`User Operation: ${operation} by ${userId}, Details: ${JSON.stringify(details)}`);
}
}
export const logManager = LogService.getInstance();
export default logManager;

View File

@@ -11,8 +11,8 @@ import "./styles/index.css";
import 'element-plus/dist/index.css'
// 引入全局组件
import HeaderBar from './components/HeaderBar/index.vue'
import DragRegion from './components/DragRegion/index.vue'
import HeaderBar from '@components/HeaderBar/index.vue'
import DragRegion from '@components/DragRegion/index.vue'
const components: Plugin = (app) => {
app.component('HeaderBar', HeaderBar);