238 lines
7.3 KiB
JavaScript
238 lines
7.3 KiB
JavaScript
// src/utils/logger.js
|
||
// 统一日志工具
|
||
|
||
// 支持开发环境或显式开启调试模式
|
||
// 生产环境下可以通过设置 REACT_APP_ENABLE_DEBUG=true 来开启调试日志
|
||
const isDevelopment =
|
||
process.env.NODE_ENV === 'development' ||
|
||
process.env.REACT_APP_ENABLE_DEBUG === 'true';
|
||
|
||
// ========== 日志级别配置 ==========
|
||
// 日志级别:error < warn < info < debug
|
||
// 默认级别:warn(只显示警告和错误)
|
||
// 可通过 localStorage.setItem('LOG_LEVEL', 'debug') 开启详细日志
|
||
const LOG_LEVELS = {
|
||
error: 0,
|
||
warn: 1,
|
||
info: 2,
|
||
debug: 3,
|
||
};
|
||
|
||
// 从 localStorage 读取日志级别(允许用户临时开启详细日志)
|
||
const getLogLevel = () => {
|
||
if (typeof window !== 'undefined' && window.localStorage) {
|
||
const level = localStorage.getItem('LOG_LEVEL');
|
||
if (level && LOG_LEVELS[level] !== undefined) {
|
||
return LOG_LEVELS[level];
|
||
}
|
||
}
|
||
// 默认只显示 warn 和 error
|
||
return LOG_LEVELS.warn;
|
||
};
|
||
|
||
// 检查是否应该输出指定级别的日志
|
||
const shouldLogLevel = (level) => {
|
||
return LOG_LEVELS[level] <= getLogLevel();
|
||
};
|
||
|
||
// ========== 日志限流配置 ==========
|
||
const LOG_THROTTLE_TIME = 1000; // 1秒内相同日志只输出一次
|
||
const recentLogs = new Map(); // 日志缓存,用于去重
|
||
const MAX_CACHE_SIZE = 100; // 最大缓存数量
|
||
|
||
/**
|
||
* 生成日志的唯一键
|
||
*/
|
||
function getLogKey(component, message) {
|
||
return `${component}:${message}`;
|
||
}
|
||
|
||
/**
|
||
* 检查是否应该输出日志(限流检查)
|
||
*/
|
||
function shouldLog(component, message) {
|
||
const key = getLogKey(component, message);
|
||
const now = Date.now();
|
||
const lastLog = recentLogs.get(key);
|
||
|
||
// 如果1秒内已经输出过相同日志,跳过
|
||
if (lastLog && now - lastLog < LOG_THROTTLE_TIME) {
|
||
return false;
|
||
}
|
||
|
||
// 记录日志时间
|
||
recentLogs.set(key, now);
|
||
|
||
// 限制缓存大小,避免内存泄漏
|
||
if (recentLogs.size > MAX_CACHE_SIZE) {
|
||
const oldestKey = recentLogs.keys().next().value;
|
||
recentLogs.delete(oldestKey);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 统一日志工具
|
||
* 开发环境:输出详细日志
|
||
* 生产环境:仅输出错误日志
|
||
*/
|
||
export const logger = {
|
||
/**
|
||
* API 相关日志
|
||
*/
|
||
api: {
|
||
/**
|
||
* 记录 API 请求
|
||
* @param {string} method - 请求方法 (GET, POST, etc.)
|
||
* @param {string} url - 请求 URL
|
||
* @param {object} data - 请求参数/body
|
||
*/
|
||
request: (method, url, data = null) => {
|
||
if (isDevelopment && shouldLog('API', `${method} ${url}`)) {
|
||
console.group(`🌐 API Request: ${method} ${url}`);
|
||
console.log('Timestamp:', new Date().toISOString());
|
||
if (data) console.log('Data:', data);
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 记录 API 响应成功
|
||
* @param {string} method - 请求方法
|
||
* @param {string} url - 请求 URL
|
||
* @param {number} status - HTTP 状态码
|
||
* @param {any} data - 响应数据
|
||
*/
|
||
response: (method, url, status, data) => {
|
||
if (isDevelopment && shouldLog('API', `${method} ${url} ${status}`)) {
|
||
console.group(`✅ API Response: ${method} ${url}`);
|
||
console.log('Status:', status);
|
||
console.log('Data:', data);
|
||
console.log('Timestamp:', new Date().toISOString());
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 记录 API 错误
|
||
* @param {string} method - 请求方法
|
||
* @param {string} url - 请求 URL
|
||
* @param {Error|any} error - 错误对象
|
||
* @param {object} requestData - 请求参数(可选)
|
||
*/
|
||
error: (method, url, error, requestData = null) => {
|
||
// API 错误始终输出,不做限流
|
||
console.group(`❌ API Error: ${method} ${url}`);
|
||
console.error('Error:', error);
|
||
console.error('Message:', error?.message || error);
|
||
if (error?.response) {
|
||
console.error('Response Status:', error.response.status);
|
||
console.error('Response Data:', error.response.data);
|
||
}
|
||
if (requestData) console.error('Request Data:', requestData);
|
||
console.error('Timestamp:', new Date().toISOString());
|
||
if (error?.stack) console.error('Stack:', error.stack);
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 组件错误日志
|
||
* @param {string} component - 组件名称
|
||
* @param {string} method - 方法名称
|
||
* @param {Error|any} error - 错误对象
|
||
* @param {object} context - 上下文信息(可选)
|
||
*/
|
||
error: (component, method, error, context = {}) => {
|
||
// 错误日志始终输出,不做限流
|
||
console.group(`🔴 Error in ${component}.${method}`);
|
||
console.error('Error:', error);
|
||
console.error('Message:', error?.message || error);
|
||
if (Object.keys(context).length > 0) {
|
||
console.error('Context:', context);
|
||
}
|
||
console.error('Timestamp:', new Date().toISOString());
|
||
if (error?.stack) console.error('Stack:', error.stack);
|
||
console.groupEnd();
|
||
},
|
||
|
||
/**
|
||
* 警告日志
|
||
* @param {string} component - 组件名称
|
||
* @param {string} message - 警告信息
|
||
* @param {object} data - 相关数据(可选)
|
||
*/
|
||
warn: (component, message, data = {}) => {
|
||
if (isDevelopment && shouldLog(component, message)) {
|
||
console.group(`⚠️ Warning: ${component}`);
|
||
console.warn('Message:', message);
|
||
if (Object.keys(data).length > 0) {
|
||
console.warn('Data:', data);
|
||
}
|
||
console.warn('Timestamp:', new Date().toISOString());
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 调试日志(仅开发环境 + LOG_LEVEL=debug)
|
||
* @param {string} component - 组件名称
|
||
* @param {string} message - 调试信息
|
||
* @param {object} data - 相关数据(可选)
|
||
*/
|
||
debug: (component, message, data = {}) => {
|
||
if (isDevelopment && shouldLogLevel('debug') && shouldLog(component, message)) {
|
||
console.group(`🐛 Debug: ${component}`);
|
||
console.log('Message:', message);
|
||
if (Object.keys(data).length > 0) {
|
||
console.log('Data:', data);
|
||
}
|
||
console.log('Timestamp:', new Date().toISOString());
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 信息日志(仅开发环境 + LOG_LEVEL>=info)
|
||
* @param {string} component - 组件名称
|
||
* @param {string} message - 信息内容
|
||
* @param {object} data - 相关数据(可选)
|
||
*/
|
||
info: (component, message, data = {}) => {
|
||
if (isDevelopment && shouldLogLevel('info') && shouldLog(component, message)) {
|
||
console.group(`ℹ️ Info: ${component}`);
|
||
console.log('Message:', message);
|
||
if (Object.keys(data).length > 0) {
|
||
console.log('Data:', data);
|
||
}
|
||
console.log('Timestamp:', new Date().toISOString());
|
||
console.groupEnd();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 设置日志级别(方便调试)
|
||
* @param {string} level - 日志级别 ('error' | 'warn' | 'info' | 'debug')
|
||
*/
|
||
setLevel: (level) => {
|
||
if (LOG_LEVELS[level] !== undefined) {
|
||
localStorage.setItem('LOG_LEVEL', level);
|
||
console.log(`[Logger] 日志级别已设置为: ${level}`);
|
||
console.log(`[Logger] 可用级别: error < warn < info < debug`);
|
||
} else {
|
||
console.error(`[Logger] 无效的日志级别: ${level}`);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取当前日志级别
|
||
*/
|
||
getLevel: () => {
|
||
const levelNum = getLogLevel();
|
||
return Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === levelNum) || 'warn';
|
||
}
|
||
};
|
||
|
||
export default logger;
|