cursor2api / src /utils /logger.js
github-actions[bot]
Update from GitHub Actions
b5e1f18
// logger.js - 统一的日志系统模块
const fs = require('fs');
const path = require('path');
// 避免循环依赖
let config = null;
// 延迟加载配置
function getConfig() {
if (!config) {
try {
config = require('../config/config');
} catch (err) {
console.error('加载配置文件失败:', err.message);
config = { log: { level: 'INFO', format: 'colored' } };
}
}
return config;
}
const LOG_LEVELS = {
ERROR: 0,
WARN: 1,
INFO: 2,
DEBUG: 3,
TRACE: 4,
HTTP: 2 // HTTP日志级别与INFO相同
};
// 默认日志级别
let currentLogLevel = LOG_LEVELS.INFO;
// 日志格式
let logFormat = 'colored'; // colored, json, text
// 带颜色的控制台输出
const COLORS = {
RESET: '\x1b[0m',
RED: '\x1b[31m',
YELLOW: '\x1b[33m',
GREEN: '\x1b[32m',
BLUE: '\x1b[34m',
CYAN: '\x1b[36m'
};
// 日志文件配置
const LOG_DIR = path.join(__dirname, '../../logs');
const LOG_FILE = path.join(LOG_DIR, 'app.log');
const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB
let logToFile = false;
// 内存中存储的日志(用于网页显示)
const memoryLogs = [];
const MAX_MEMORY_LOGS = 1000; // 内存中最多保存的日志条数
// 确保日志目录存在
function ensureLogDirExists() {
try {
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
return true;
} catch (err) {
console.error(`创建日志目录失败: ${err.message}`);
return false;
}
}
// 初始化文件日志
function initFileLogging() {
const conf = getConfig();
if (process.env.LOG_TO_FILE === 'true' || (conf.log && conf.log.toFile)) {
if (ensureLogDirExists()) {
logToFile = true;
// 检查日志文件大小,如果超过最大值则进行轮转
if (fs.existsSync(LOG_FILE)) {
const stats = fs.statSync(LOG_FILE);
if (stats.size > MAX_LOG_SIZE) {
rotateLogFile();
}
}
return true;
}
}
return false;
}
// 日志文件轮转
function rotateLogFile() {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const newLogFile = path.join(LOG_DIR, `app-${timestamp}.log`);
if (fs.existsSync(LOG_FILE)) {
fs.renameSync(LOG_FILE, newLogFile);
}
// 清理旧日志文件,保留最近10个
const logFiles = fs.readdirSync(LOG_DIR)
.filter(file => file.startsWith('app-') && file.endsWith('.log'))
.sort()
.reverse();
if (logFiles.length > 10) {
logFiles.slice(10).forEach(file => {
try {
fs.unlinkSync(path.join(LOG_DIR, file));
} catch (err) {
console.error(`删除旧日志文件失败: ${err.message}`);
}
});
}
} catch (err) {
console.error(`日志文件轮转失败: ${err.message}`);
logToFile = false;
}
}
// 添加日志到内存
function addLogToMemory(level, timestamp, ...args) {
// 将日志对象添加到内存数组
const logEntry = {
level,
timestamp,
message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ')
};
memoryLogs.unshift(logEntry); // 新日志添加到数组开头
// 保持数组在最大长度以内
if (memoryLogs.length > MAX_MEMORY_LOGS) {
memoryLogs.pop(); // 移除最旧的日志
}
}
// 将日志写入文件
function writeLogToFile(level, timestamp, ...args) {
if (!logToFile) return;
try {
let logEntry;
if (logFormat === 'json') {
// JSON格式
const data = args.map(arg => typeof arg === 'object' ? arg : String(arg));
const logObject = {
level,
timestamp,
message: data.length === 1 ? data[0] : data
};
logEntry = JSON.stringify(logObject) + '\n';
} else {
// 文本格式
logEntry = `[${level}] ${timestamp} ${args.map(arg =>
typeof arg === 'object' ? JSON.stringify(arg) : arg
).join(' ')}\n`;
}
fs.appendFileSync(LOG_FILE, logEntry);
// 检查文件大小,必要时进行轮转
const stats = fs.statSync(LOG_FILE);
if (stats.size > MAX_LOG_SIZE) {
rotateLogFile();
}
} catch (err) {
console.error(`写入日志文件失败: ${err.message}`);
logToFile = false;
}
}
// 获取时间戳
function getTimestamp() {
return new Date().toISOString();
}
// 设置日志级别
function setLogLevel(level) {
if (typeof level === 'string') {
level = level.toUpperCase();
if (LOG_LEVELS[level] !== undefined) {
currentLogLevel = LOG_LEVELS[level];
} else {
error(`无效的日志级别: ${level}`);
}
} else if (typeof level === 'number' && level >= 0 && level <= 4) {
currentLogLevel = level;
} else {
error(`无效的日志级别: ${level}`);
}
}
// 设置日志格式
function setLogFormat(format) {
const validFormats = ['colored', 'json', 'text'];
if (validFormats.includes(format)) {
logFormat = format;
return true;
} else {
error(`无效的日志格式: ${format}`);
return false;
}
}
// 格式化控制台日志
function formatConsoleLog(level, timestamp, color, ...args) {
if (logFormat === 'json') {
// JSON格式
const data = args.map(arg => typeof arg === 'object' ? arg : String(arg));
return JSON.stringify({
level,
timestamp,
message: data.length === 1 ? data[0] : data
});
} else if (logFormat === 'text') {
// 纯文本格式(无颜色)
return `[${level}] ${timestamp} ${args.join(' ')}`;
} else {
// 默认:带颜色格式
return `${color}[${level}] ${timestamp}${COLORS.RESET} ${args.join(' ')}`;
}
}
// 错误日志
function error(...args) {
if (currentLogLevel >= LOG_LEVELS.ERROR) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('ERROR', timestamp, COLORS.RED, ...args);
console.error(formattedLog);
writeLogToFile('ERROR', timestamp, ...args);
addLogToMemory('ERROR', timestamp, ...args);
}
}
// 警告日志
function warn(...args) {
if (currentLogLevel >= LOG_LEVELS.WARN) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('WARN', timestamp, COLORS.YELLOW, ...args);
console.warn(formattedLog);
writeLogToFile('WARN', timestamp, ...args);
addLogToMemory('WARN', timestamp, ...args);
}
}
// 信息日志
function info(...args) {
if (currentLogLevel >= LOG_LEVELS.INFO) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('INFO', timestamp, COLORS.GREEN, ...args);
console.log(formattedLog);
writeLogToFile('INFO', timestamp, ...args);
addLogToMemory('INFO', timestamp, ...args);
}
}
// 调试日志
function debug(...args) {
if (currentLogLevel >= LOG_LEVELS.DEBUG) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('DEBUG', timestamp, COLORS.BLUE, ...args);
console.log(formattedLog);
writeLogToFile('DEBUG', timestamp, ...args);
addLogToMemory('DEBUG', timestamp, ...args);
}
}
// 跟踪日志
function trace(...args) {
if (currentLogLevel >= LOG_LEVELS.TRACE) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('TRACE', timestamp, COLORS.CYAN, ...args);
console.log(formattedLog);
writeLogToFile('TRACE', timestamp, ...args);
addLogToMemory('TRACE', timestamp, ...args);
}
}
// HTTP请求日志 (特殊处理,方便筛选)
function http(...args) {
if (currentLogLevel >= LOG_LEVELS.INFO) {
const timestamp = getTimestamp();
const formattedLog = formatConsoleLog('HTTP', timestamp, COLORS.CYAN, ...args);
console.log(formattedLog);
writeLogToFile('HTTP', timestamp, ...args);
addLogToMemory('HTTP', timestamp, ...args);
}
}
// 获取内存中的日志
function getLogs(filter = {}) {
let filteredLogs = [...memoryLogs];
// 按日志级别筛选
if (filter.level) {
filteredLogs = filteredLogs.filter(log => log.level === filter.level);
}
// 按时间范围筛选
if (filter.startTime) {
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) >= new Date(filter.startTime));
}
if (filter.endTime) {
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) <= new Date(filter.endTime));
}
// 按关键词搜索
if (filter.search) {
const searchTerm = filter.search.toLowerCase();
filteredLogs = filteredLogs.filter(log =>
log.message.toLowerCase().includes(searchTerm) ||
log.level.toLowerCase().includes(searchTerm)
);
}
// 分页
const page = filter.page || 1;
const pageSize = filter.pageSize || 100;
const start = (page - 1) * pageSize;
const end = start + pageSize;
return {
logs: filteredLogs.slice(start, end),
total: filteredLogs.length,
page,
pageSize
};
}
// 清除内存日志
function clearMemoryLogs() {
memoryLogs.length = 0;
info('内存日志已清除');
}
// 初始化配置
function initialize() {
try {
const conf = getConfig();
// 初始化日志级别
const envLevel = process.env.LOG_LEVEL;
if (envLevel) {
setLogLevel(envLevel);
} else if (conf && conf.log && conf.log.level) {
setLogLevel(conf.log.level);
}
// 初始化日志格式
const envFormat = process.env.LOG_FORMAT;
if (envFormat) {
setLogFormat(envFormat);
} else if (conf && conf.log && conf.log.format) {
setLogFormat(conf.log.format);
}
// 初始化文件日志
initFileLogging();
} catch (err) {
console.error(`初始化日志系统出错: ${err.message}`);
}
}
// 初始化
initialize();
module.exports = {
LOG_LEVELS,
setLogLevel,
setLogFormat,
error,
warn,
info,
debug,
trace,
http,
// 暴露文件日志相关方法
enableFileLogging: () => {
if (ensureLogDirExists()) {
logToFile = true;
info('文件日志已启用');
return true;
}
return false;
},
disableFileLogging: () => {
logToFile = false;
info('文件日志已禁用');
},
rotateLogFile,
// 添加内存日志相关方法
getLogs,
clearMemoryLogs
};