后端改为使用日志记录器管理日志
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* 日志级别枚举
|
||||
*/
|
||||
const LogLevel = {
|
||||
ERROR: 0,
|
||||
WARN: 1,
|
||||
INFO: 2,
|
||||
DEBUG: 3,
|
||||
TRACE: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* 日志级别名称映射
|
||||
*/
|
||||
const LogLevelNames = {
|
||||
[LogLevel.ERROR]: 'ERROR',
|
||||
[LogLevel.WARN]: 'WARN',
|
||||
[LogLevel.INFO]: 'INFO',
|
||||
[LogLevel.DEBUG]: 'DEBUG',
|
||||
[LogLevel.TRACE]: 'TRACE'
|
||||
};
|
||||
|
||||
/**
|
||||
* 日志级别颜色映射
|
||||
*/
|
||||
const LogLevelColors = {
|
||||
[LogLevel.ERROR]: '\x1b[31m', // 红色
|
||||
[LogLevel.WARN]: '\x1b[33m', // 黄色
|
||||
[LogLevel.INFO]: '\x1b[36m', // 青色
|
||||
[LogLevel.DEBUG]: '\x1b[35m', // 紫色
|
||||
[LogLevel.TRACE]: '\x1b[90m' // 灰色
|
||||
};
|
||||
|
||||
/**
|
||||
* 模块颜色映射 - 为不同模块设置不同颜色
|
||||
*/
|
||||
const ModuleColors = {
|
||||
'Server': '\x1b[32m', // 绿色
|
||||
'Start': '\x1b[34m', // 蓝色
|
||||
'PixivBackend': '\x1b[35m', // 紫色
|
||||
'PixivAuth': '\x1b[36m', // 青色
|
||||
'TaskManager': '\x1b[33m', // 黄色
|
||||
'ImageCache': '\x1b[37m', // 白色
|
||||
'HistoryManager': '\x1b[90m', // 灰色
|
||||
'ProxyConfig': '\x1b[95m', // 亮紫色
|
||||
'Download': '\x1b[93m', // 亮黄色
|
||||
'Artwork': '\x1b[96m', // 亮青色
|
||||
'Artist': '\x1b[92m', // 亮绿色
|
||||
'Repository': '\x1b[94m', // 亮蓝色
|
||||
'ErrorHandler': '\x1b[91m', // 亮红色
|
||||
'API': '\x1b[97m', // 亮白色
|
||||
'FileManager': '\x1b[98m', // 亮青色
|
||||
'ProgressManager': '\x1b[99m', // 亮紫色
|
||||
'Default': '\x1b[39m' // 默认颜色
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置颜色
|
||||
*/
|
||||
const RESET_COLOR = '\x1b[0m';
|
||||
|
||||
/**
|
||||
* 日志图标映射
|
||||
*/
|
||||
const LogLevelIcons = {
|
||||
[LogLevel.ERROR]: '❌',
|
||||
[LogLevel.WARN]: '⚠️',
|
||||
[LogLevel.INFO]: 'ℹ️',
|
||||
[LogLevel.DEBUG]: '🔧',
|
||||
[LogLevel.TRACE]: '🔍'
|
||||
};
|
||||
|
||||
class Logger {
|
||||
constructor(options = {}) {
|
||||
this.level = options.level || LogLevel.INFO;
|
||||
this.enableConsole = options.enableConsole !== false;
|
||||
this.enableFile = options.enableFile || false;
|
||||
this.logDir = options.logDir || path.join(__dirname, '../logs');
|
||||
this.maxFileSize = options.maxFileSize || 10 * 1024 * 1024; // 10MB
|
||||
this.maxFiles = options.maxFiles || 5;
|
||||
this.enableColors = options.enableColors !== false;
|
||||
this.module = options.module || 'App';
|
||||
|
||||
// 确保日志目录存在
|
||||
if (this.enableFile) {
|
||||
this.ensureLogDir();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保日志目录存在
|
||||
*/
|
||||
ensureLogDir() {
|
||||
if (!fs.existsSync(this.logDir)) {
|
||||
fs.mkdirSync(this.logDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间字符串
|
||||
*/
|
||||
getTimeString() {
|
||||
const now = new Date();
|
||||
return now.toLocaleTimeString('zh-CN', {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期字符串
|
||||
*/
|
||||
getDateString() {
|
||||
const now = new Date();
|
||||
return now.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日志消息
|
||||
*/
|
||||
formatMessage(level, message, data = null) {
|
||||
const timeStr = this.getTimeString();
|
||||
const levelName = LogLevelNames[level];
|
||||
const icon = LogLevelIcons[level];
|
||||
|
||||
let formattedMessage = `[${timeStr}] [${levelName}] [${this.module}] ${icon} ${message}`;
|
||||
|
||||
if (data !== null && data !== undefined) {
|
||||
if (typeof data === 'object') {
|
||||
formattedMessage += ` ${JSON.stringify(data, null, 2)}`;
|
||||
} else {
|
||||
formattedMessage += ` ${data}`;
|
||||
}
|
||||
}
|
||||
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入文件日志
|
||||
*/
|
||||
writeToFile(message) {
|
||||
if (!this.enableFile) return;
|
||||
|
||||
const dateStr = this.getDateString();
|
||||
const logFile = path.join(this.logDir, `${dateStr}.log`);
|
||||
|
||||
try {
|
||||
// 检查文件大小
|
||||
if (fs.existsSync(logFile)) {
|
||||
const stats = fs.statSync(logFile);
|
||||
if (stats.size > this.maxFileSize) {
|
||||
this.rotateLogFile(logFile);
|
||||
}
|
||||
}
|
||||
|
||||
fs.appendFileSync(logFile, message + '\n', 'utf8');
|
||||
} catch (error) {
|
||||
// 如果写入文件失败,至少输出到控制台
|
||||
if (this.enableConsole) {
|
||||
console.error('Failed to write to log file:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮转日志文件
|
||||
*/
|
||||
rotateLogFile(logFile) {
|
||||
try {
|
||||
// 删除最旧的文件
|
||||
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
||||
const oldFile = `${logFile}.${i}`;
|
||||
const newFile = `${logFile}.${i + 1}`;
|
||||
if (fs.existsSync(oldFile)) {
|
||||
if (i === this.maxFiles - 1) {
|
||||
fs.unlinkSync(oldFile);
|
||||
} else {
|
||||
fs.renameSync(oldFile, newFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重命名当前文件
|
||||
fs.renameSync(logFile, `${logFile}.1`);
|
||||
} catch (error) {
|
||||
// 如果轮转失败,删除当前文件
|
||||
try {
|
||||
fs.unlinkSync(logFile);
|
||||
} catch (e) {
|
||||
// 忽略删除错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出到控制台
|
||||
*/
|
||||
writeToConsole(message, level) {
|
||||
if (!this.enableConsole) return;
|
||||
|
||||
if (this.enableColors && level !== undefined) {
|
||||
const levelColor = LogLevelColors[level];
|
||||
const moduleColor = ModuleColors[this.module] || ModuleColors['Default'];
|
||||
|
||||
// 解析消息,为模块名添加颜色
|
||||
const coloredMessage = message.replace(
|
||||
new RegExp(`\\[${this.module}\\]`, 'g'),
|
||||
`${moduleColor}[${this.module}]${RESET_COLOR}`
|
||||
);
|
||||
|
||||
console.log(`${levelColor}${coloredMessage}${RESET_COLOR}`);
|
||||
} else {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志
|
||||
*/
|
||||
log(level, message, data = null) {
|
||||
if (level > this.level) return;
|
||||
|
||||
const formattedMessage = this.formatMessage(level, message, data);
|
||||
|
||||
this.writeToConsole(formattedMessage, level);
|
||||
this.writeToFile(formattedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误日志
|
||||
*/
|
||||
error(message, data = null) {
|
||||
this.log(LogLevel.ERROR, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 警告日志
|
||||
*/
|
||||
warn(message, data = null) {
|
||||
this.log(LogLevel.WARN, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 信息日志
|
||||
*/
|
||||
info(message, data = null) {
|
||||
this.log(LogLevel.INFO, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试日志
|
||||
*/
|
||||
debug(message, data = null) {
|
||||
this.log(LogLevel.DEBUG, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跟踪日志
|
||||
*/
|
||||
trace(message, data = null) {
|
||||
this.log(LogLevel.TRACE, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子logger
|
||||
*/
|
||||
child(module) {
|
||||
return new Logger({
|
||||
level: this.level,
|
||||
enableConsole: this.enableConsole,
|
||||
enableFile: this.enableFile,
|
||||
logDir: this.logDir,
|
||||
maxFileSize: this.maxFileSize,
|
||||
maxFiles: this.maxFiles,
|
||||
enableColors: this.enableColors,
|
||||
module: module
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日志级别
|
||||
*/
|
||||
setLevel(level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用控制台输出
|
||||
*/
|
||||
setConsoleOutput(enabled) {
|
||||
this.enableConsole = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用文件输出
|
||||
*/
|
||||
setFileOutput(enabled) {
|
||||
this.enableFile = enabled;
|
||||
if (enabled) {
|
||||
this.ensureLogDir();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建默认logger实例
|
||||
const defaultLogger = new Logger({
|
||||
level: process.env.LOG_LEVEL ? LogLevel[process.env.LOG_LEVEL.toUpperCase()] : LogLevel.INFO,
|
||||
enableConsole: true,
|
||||
enableFile: process.env.LOG_TO_FILE === 'true',
|
||||
enableColors: process.env.LOG_COLORS !== 'false'
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Logger,
|
||||
LogLevel,
|
||||
LogLevelNames,
|
||||
defaultLogger
|
||||
};
|
||||
Reference in New Issue
Block a user