Files
pixiv/backend/utils/logger.js
T

324 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
};