// 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;