feat: 重写日志服务
This commit is contained in:
@@ -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({
|
// 转换为Promise形式的fs方法
|
||||||
appenders: {
|
const readdirAsync = promisify(fs.readdir);
|
||||||
out: {
|
const statAsync = promisify(fs.stat);
|
||||||
type: 'stdout'
|
const unlinkAsync = promisify(fs.unlink);
|
||||||
},
|
|
||||||
app: {
|
class LogService {
|
||||||
type: 'file',
|
private static _instance: LogService;
|
||||||
filename: 'logs/app.log',
|
|
||||||
backups: 3,
|
// 日志保留天数,默认7天
|
||||||
compress: false,
|
private LOG_RETENTION_DAYS = 7;
|
||||||
encoding: 'utf-8',
|
|
||||||
layout: {
|
// 清理间隔,默认24小时(毫秒)
|
||||||
type: 'pattern',
|
private readonly CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
||||||
pattern: '[%d{yyyy-MM-dd hh:mm:ss.SSS}] [%p] %m'
|
|
||||||
},
|
private constructor() {
|
||||||
keepFileExt: true
|
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: {
|
// 配置electron-log
|
||||||
default: {
|
log.transports.file.resolvePathFn = () => {
|
||||||
appenders: ['out', 'app'],
|
// 使用当前日期作为日志文件名,格式为 YYYY-MM-DD.log
|
||||||
level: 'debug'
|
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;
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import "./styles/index.css";
|
|||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
|
|
||||||
// 引入全局组件
|
// 引入全局组件
|
||||||
import HeaderBar from './components/HeaderBar/index.vue'
|
import HeaderBar from '@components/HeaderBar/index.vue'
|
||||||
import DragRegion from './components/DragRegion/index.vue'
|
import DragRegion from '@components/DragRegion/index.vue'
|
||||||
|
|
||||||
const components: Plugin = (app) => {
|
const components: Plugin = (app) => {
|
||||||
app.component('HeaderBar', HeaderBar);
|
app.component('HeaderBar', HeaderBar);
|
||||||
|
|||||||
Reference in New Issue
Block a user