后端改为使用日志记录器管理日志

This commit is contained in:
2025-08-31 18:55:22 +08:00
parent ad5dfc64cb
commit a09d6cab0e
30 changed files with 962 additions and 323 deletions
+44 -39
View File
@@ -1,4 +1,9 @@
const path = require('path');
const { defaultLogger } = require('../utils/logger');
// 创建logger实例
const logger = defaultLogger.child('ErrorHandler');
/**
* 错误处理工具类 - 专门处理打包后的权限问题
@@ -19,7 +24,7 @@ class ErrorHandler {
};
// 记录错误信息
console.error(`文件系统错误 [${operation}]:`, {
logger.error(`文件系统错误 [${operation}]:`, {
filePath: filePath,
errorCode: error.code,
errorMessage: error.message,
@@ -52,25 +57,25 @@ class ErrorHandler {
static handlePermissionError(errorInfo) {
const { filePath, isPkg, platform } = errorInfo;
console.error('权限错误 (EPERM) 解决方案:');
logger.error('权限错误 (EPERM) 解决方案:');
if (platform === 'win32') {
console.error('Windows 权限问题解决方案:');
console.error('1. 以管理员身份运行程序');
console.error('2. 检查文件/目录权限');
console.error('3. 检查防病毒软件是否阻止访问');
console.error('4. 检查文件是否被其他程序占用');
logger.error('Windows 权限问题解决方案:');
logger.error('1. 以管理员身份运行程序');
logger.error('2. 检查文件/目录权限');
logger.error('3. 检查防病毒软件是否阻止访问');
logger.error('4. 检查文件是否被其他程序占用');
if (isPkg) {
console.error('5. 打包环境特殊处理:');
console.error(' - 确保程序有写入权限');
console.error(' - 尝试使用用户目录而不是程序目录');
logger.error('5. 打包环境特殊处理:');
logger.error(' - 确保程序有写入权限');
logger.error(' - 尝试使用用户目录而不是程序目录');
}
} else {
console.error('Unix/Linux 权限问题解决方案:');
console.error('1. 检查文件权限: chmod 755 <file>');
console.error('2. 检查目录权限: chmod 755 <directory>');
console.error('3. 检查用户权限');
logger.error('Unix/Linux 权限问题解决方案:');
logger.error('1. 检查文件权限: chmod 755 <file>');
logger.error('2. 检查目录权限: chmod 755 <directory>');
logger.error('3. 检查用户权限');
}
return {
@@ -87,14 +92,14 @@ class ErrorHandler {
static handleAccessError(errorInfo) {
const { filePath, platform } = errorInfo;
console.error('访问错误 (EACCES) 解决方案:');
console.error('1. 检查文件/目录是否存在');
console.error('2. 检查用户是否有访问权限');
console.error('3. 检查文件系统权限');
logger.error('访问错误 (EACCES) 解决方案:');
logger.error('1. 检查文件/目录是否存在');
logger.error('2. 检查用户是否有访问权限');
logger.error('3. 检查文件系统权限');
if (platform === 'win32') {
console.error('4. 检查 Windows 安全设置');
console.error('5. 尝试以管理员身份运行');
logger.error('4. 检查 Windows 安全设置');
logger.error('5. 尝试以管理员身份运行');
}
return {
@@ -111,11 +116,11 @@ class ErrorHandler {
static handleBusyError(errorInfo) {
const { filePath } = errorInfo;
console.error('文件占用错误 (EBUSY) 解决方案:');
console.error('1. 关闭可能占用文件的程序');
console.error('2. 等待文件释放后重试');
console.error('3. 重启相关程序');
console.error('4. 检查是否有其他进程在使用文件');
logger.error('文件占用错误 (EBUSY) 解决方案:');
logger.error('1. 关闭可能占用文件的程序');
logger.error('2. 等待文件释放后重试');
logger.error('3. 重启相关程序');
logger.error('4. 检查是否有其他进程在使用文件');
return {
type: 'BUSY_ERROR',
@@ -132,10 +137,10 @@ class ErrorHandler {
static handleNotFoundError(errorInfo) {
const { filePath } = errorInfo;
console.error('文件不存在错误 (ENOENT) 解决方案:');
console.error('1. 检查文件路径是否正确');
console.error('2. 确保目录存在');
console.error('3. 检查文件名是否正确');
logger.error('文件不存在错误 (ENOENT) 解决方案:');
logger.error('1. 检查文件路径是否正确');
logger.error('2. 确保目录存在');
logger.error('3. 检查文件名是否正确');
return {
type: 'NOT_FOUND_ERROR',
@@ -151,9 +156,9 @@ class ErrorHandler {
static handleIsDirectoryError(errorInfo) {
const { filePath } = errorInfo;
console.error('是目录错误 (EISDIR) 解决方案:');
console.error('1. 检查路径是否指向目录而不是文件');
console.error('2. 确保使用正确的文件路径');
logger.error('是目录错误 (EISDIR) 解决方案:');
logger.error('1. 检查路径是否指向目录而不是文件');
logger.error('2. 确保使用正确的文件路径');
return {
type: 'IS_DIRECTORY_ERROR',
@@ -169,9 +174,9 @@ class ErrorHandler {
static handleNotDirectoryError(errorInfo) {
const { filePath } = errorInfo;
console.error('不是目录错误 (ENOTDIR) 解决方案:');
console.error('1. 检查路径是否指向文件而不是目录');
console.error('2. 确保使用正确的目录路径');
logger.error('不是目录错误 (ENOTDIR) 解决方案:');
logger.error('1. 检查路径是否指向文件而不是目录');
logger.error('2. 确保使用正确的目录路径');
return {
type: 'NOT_DIRECTORY_ERROR',
@@ -187,10 +192,10 @@ class ErrorHandler {
static handleGenericError(errorInfo) {
const { filePath, errorCode, errorMessage } = errorInfo;
console.error(`通用文件系统错误 (${errorCode}): ${errorMessage}`);
console.error('1. 检查文件系统状态');
console.error('2. 检查磁盘空间');
console.error('3. 检查文件系统权限');
logger.error(`通用文件系统错误 (${errorCode}): ${errorMessage}`);
logger.error('1. 检查文件系统状态');
logger.error('2. 检查磁盘空间');
logger.error('3. 检查文件系统权限');
return {
type: 'GENERIC_ERROR',
+16 -11
View File
@@ -1,5 +1,10 @@
const fs = require('fs-extra');
const path = require('path');
const { defaultLogger } = require('./logger');
// 创建logger实例
const logger = defaultLogger.child('FileUtils');
/**
* 文件操作工具类 - 确保与 pkg 打包兼容
@@ -22,7 +27,7 @@ class FileUtils {
await nativeFs.unlink(filePath);
return true;
} catch (nativeError) {
console.error(`文件删除失败: ${filePath}`, nativeError.message);
logger.error(`文件删除失败: ${filePath}`, nativeError.message);
return false;
}
}
@@ -44,7 +49,7 @@ class FileUtils {
await nativeFs.mkdir(dirPath, { recursive: true });
return true;
} catch (nativeError) {
console.error(`目录创建失败: ${dirPath}`, nativeError.message);
logger.error(`目录创建失败: ${dirPath}`, nativeError.message);
return false;
}
}
@@ -70,7 +75,7 @@ class FileUtils {
return true;
}
} catch (error) {
console.error(`增强目录创建失败: ${dirPath}`, error.message);
logger.error(`增强目录创建失败: ${dirPath}`, error.message);
return false;
}
}
@@ -102,7 +107,7 @@ class FileUtils {
// 验证创建是否成功
await fs.access(currentPath);
} catch (mkdirError) {
console.error(`创建目录失败: ${currentPath}`, mkdirError.message);
logger.error(`创建目录失败: ${currentPath}`, mkdirError.message);
throw mkdirError;
}
} else {
@@ -113,7 +118,7 @@ class FileUtils {
return true;
} catch (error) {
console.error(`递归创建目录失败: ${dirPath}`, error.message);
logger.error(`递归创建目录失败: ${dirPath}`, error.message);
return false;
}
}
@@ -137,7 +142,7 @@ class FileUtils {
// 尝试删除现有文件
await fs.remove(filePath);
} catch (removeError) {
console.warn(`删除现有文件失败: ${filePath}`, removeError.message);
logger.warn(`删除现有文件失败: ${filePath}`, removeError.message);
// 继续尝试写入,可能会覆盖
}
}
@@ -150,7 +155,7 @@ class FileUtils {
return true;
} catch (error) {
console.error(`安全写入文件失败: ${filePath}`, error.message);
logger.error(`安全写入文件失败: ${filePath}`, error.message);
return false;
}
}
@@ -173,14 +178,14 @@ class FileUtils {
try {
await fs.remove(filePath);
} catch (removeError) {
console.warn(`删除现有文件失败: ${filePath}`, removeError.message);
logger.warn(`删除现有文件失败: ${filePath}`, removeError.message);
}
}
// 创建写入流
return fs.createWriteStream(filePath);
} catch (error) {
console.error(`创建写入流失败: ${filePath}`, error.message);
logger.error(`创建写入流失败: ${filePath}`, error.message);
throw error;
}
}
@@ -217,7 +222,7 @@ class FileUtils {
const nativeFs = require('fs').promises;
return await nativeFs.readdir(dirPath);
} catch (nativeError) {
console.error(`读取目录失败: ${dirPath}`, nativeError.message);
logger.error(`读取目录失败: ${dirPath}`, nativeError.message);
return [];
}
}
@@ -239,7 +244,7 @@ class FileUtils {
await nativeFs.writeFile(filePath, jsonString, 'utf8');
return true;
} catch (nativeError) {
console.error(`JSON 写入失败: ${filePath}`, nativeError.message);
logger.error(`JSON 写入失败: ${filePath}`, nativeError.message);
return false;
}
}
+324
View File
@@ -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
};