Skip to main content
Glama
ErrorLogger.js6.73 kB
/** * Error Logger - 에러 로깅 및 추적 유틸리티 * 에러 발생 시 로그 저장 및 분석 기능 제공 */ import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export class ErrorLogger { constructor() { this.logDir = path.join(__dirname, '../../data/logs'); this.errorLogFile = path.join(this.logDir, 'errors.json'); this.maxLogEntries = 1000; // 최대 로그 엔트리 수 this.initializeLogger(); } async initializeLogger() { try { await fs.mkdir(this.logDir, { recursive: true }); // 로그 파일이 없으면 생성 try { await fs.access(this.errorLogFile); } catch (error) { await fs.writeFile(this.errorLogFile, JSON.stringify({ logs: [] }, null, 2)); } } catch (error) { console.error('Failed to initialize ErrorLogger:', error); } } /** * 에러 로그 기록 * @param {string} toolName - 에러가 발생한 도구명 * @param {string} errorMessage - 에러 메시지 * @param {Object} context - 에러 발생 컨텍스트 */ async logError(toolName, errorMessage, context = {}) { const errorEntry = { id: this.generateErrorId(), timestamp: new Date().toISOString(), toolName: toolName, message: errorMessage, context: context, stack: context.stack || new Error().stack, severity: this.determineSeverity(errorMessage), environment: { nodeVersion: process.version, platform: process.platform, memoryUsage: process.memoryUsage() } }; try { await this.saveErrorLog(errorEntry); console.error(`Error logged [${errorEntry.id}]: ${toolName} - ${errorMessage}`); } catch (saveError) { console.error('Failed to save error log:', saveError); } } /** * 에러 로그 저장 */ async saveErrorLog(errorEntry) { try { const logData = JSON.parse(await fs.readFile(this.errorLogFile, 'utf8')); // 새 에러 추가 logData.logs.unshift(errorEntry); // 최대 엔트리 수 제한 if (logData.logs.length > this.maxLogEntries) { logData.logs = logData.logs.slice(0, this.maxLogEntries); } await fs.writeFile(this.errorLogFile, JSON.stringify(logData, null, 2)); } catch (error) { console.error('Error saving log:', error); } } /** * 에러 로그 조회 * @param {Object} filters - 필터 조건 * @returns {Array} 에러 로그 목록 */ async getErrorLogs(filters = {}) { try { const logData = JSON.parse(await fs.readFile(this.errorLogFile, 'utf8')); let logs = logData.logs; // 필터 적용 if (filters.toolName) { logs = logs.filter(log => log.toolName === filters.toolName); } if (filters.severity) { logs = logs.filter(log => log.severity === filters.severity); } if (filters.fromDate) { const fromDate = new Date(filters.fromDate); logs = logs.filter(log => new Date(log.timestamp) >= fromDate); } if (filters.limit) { logs = logs.slice(0, filters.limit); } return logs; } catch (error) { console.error('Failed to get error logs:', error); return []; } } /** * 에러 통계 조회 * @returns {Object} 에러 통계 데이터 */ async getErrorStatistics() { try { const logs = await this.getErrorLogs(); const stats = { totalErrors: logs.length, errorsByTool: {}, errorsBySeverity: {}, recentErrors: logs.slice(0, 10), errorTrends: this.calculateErrorTrends(logs) }; // 도구별 에러 수 logs.forEach(log => { stats.errorsByTool[log.toolName] = (stats.errorsByTool[log.toolName] || 0) + 1; }); // 심각도별 에러 수 logs.forEach(log => { stats.errorsBySeverity[log.severity] = (stats.errorsBySeverity[log.severity] || 0) + 1; }); return stats; } catch (error) { console.error('Failed to get error statistics:', error); return { totalErrors: 0, errorsByTool: {}, errorsBySeverity: {}, recentErrors: [], errorTrends: [] }; } } /** * 심각한 에러 알림 확인 * @returns {Array} 알림이 필요한 에러 목록 */ async getCriticalErrorAlerts() { const recentLogs = await this.getErrorLogs({ severity: 'critical', limit: 10 }); const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); const recentCriticalErrors = recentLogs.filter(log => new Date(log.timestamp) > oneHourAgo ); return recentCriticalErrors.map(log => ({ id: log.id, toolName: log.toolName, message: log.message, timestamp: log.timestamp, alertType: 'critical_error' })); } /** * 로그 파일 정리 */ async cleanupLogs() { try { const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const logs = await this.getErrorLogs(); const recentLogs = logs.filter(log => new Date(log.timestamp) > thirtyDaysAgo ); await fs.writeFile(this.errorLogFile, JSON.stringify({ logs: recentLogs }, null, 2)); console.log(`Log cleanup completed. Kept ${recentLogs.length} recent logs.`); } catch (error) { console.error('Failed to cleanup logs:', error); } } // 유틸리티 메서드들 generateErrorId() { return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } determineSeverity(errorMessage) { const criticalKeywords = ['fatal', 'critical', 'security', 'corruption']; const warningKeywords = ['validation', 'timeout', 'network']; const lowerMessage = errorMessage.toLowerCase(); if (criticalKeywords.some(keyword => lowerMessage.includes(keyword))) { return 'critical'; } else if (warningKeywords.some(keyword => lowerMessage.includes(keyword))) { return 'warning'; } return 'error'; } calculateErrorTrends(logs) { // 최근 7일간의 일별 에러 수 const trends = []; for (let i = 6; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); const dateString = date.toISOString().split('T')[0]; const dayErrors = logs.filter(log => log.timestamp.startsWith(dateString) ).length; trends.push({ date: dateString, errorCount: dayErrors }); } return trends; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/foswmine/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server