后端改为使用日志记录器管理日志
This commit is contained in:
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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