UNPKG

recoder-analytics

Version:

Comprehensive analytics and monitoring for the Recoder.xyz ecosystem

657 lines 22.9 kB
"use strict"; /** * Production Monitoring System for Recoder.xyz Ecosystem * * Comprehensive monitoring, analytics, and alerting across CLI, Web, and Extension */ Object.defineProperty(exports, "__esModule", { value: true }); exports.productionMonitoring = exports.ProductionMonitoring = void 0; const events_1 = require("events"); class ProductionMonitoring extends events_1.EventEmitter { constructor() { super(); this.metrics = new Map(); this.alerts = new Map(); this.healthChecks = new Map(); this.isMonitoring = false; this.initializeDefaultAlerts(); this.initializeHealthChecks(); } static getInstance() { if (!ProductionMonitoring.instance) { ProductionMonitoring.instance = new ProductionMonitoring(); } return ProductionMonitoring.instance; } /** * Start monitoring system */ startMonitoring() { if (this.isMonitoring) { return; } this.isMonitoring = true; // Start periodic health checks this.monitoringInterval = setInterval(() => { this.performHealthChecks(); }, 30000); // Every 30 seconds // Start metric collection this.startMetricCollection(); this.emit('monitoring:started'); console.log('🔍 Production monitoring started'); } /** * Stop monitoring system */ stopMonitoring() { if (!this.isMonitoring) { return; } this.isMonitoring = false; if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = undefined; } this.emit('monitoring:stopped'); console.log('🔍 Production monitoring stopped'); } /** * Record a metric */ recordMetric(metric) { const metricData = { ...metric, timestamp: Date.now() }; // Store metric const key = `${metric.platform}:${metric.name}`; if (!this.metrics.has(key)) { this.metrics.set(key, []); } const metrics = this.metrics.get(key); metrics.push(metricData); // Keep only last 1000 metrics per key if (metrics.length > 1000) { metrics.splice(0, metrics.length - 1000); } // Check alerts this.checkAlerts(metricData); // Emit metric event this.emit('metric:recorded', metricData); // Send to external monitoring services this.sendToExternalServices(metricData); } /** * Initialize default alert rules */ initializeDefaultAlerts() { const defaultAlerts = [ { id: 'high_error_rate', name: 'High Error Rate', metric: 'error_rate', condition: 'gt', threshold: 5, // 5% duration: 300000, // 5 minutes severity: 'high', channels: ['slack', 'email'], enabled: true }, { id: 'slow_response_time', name: 'Slow Response Time', metric: 'response_time', condition: 'gt', threshold: 5000, // 5 seconds duration: 180000, // 3 minutes severity: 'medium', channels: ['slack'], enabled: true }, { id: 'low_ai_provider_success', name: 'Low AI Provider Success Rate', metric: 'ai_success_rate', condition: 'lt', threshold: 90, // 90% duration: 600000, // 10 minutes severity: 'high', channels: ['slack', 'email', 'pagerduty'], enabled: true }, { id: 'high_token_usage', name: 'High Token Usage', metric: 'token_usage_per_hour', condition: 'gt', threshold: 1000000, // 1M tokens per hour duration: 0, // Immediate severity: 'medium', channels: ['slack'], enabled: true }, { id: 'low_system_uptime', name: 'Low System Uptime', metric: 'uptime_percentage', condition: 'lt', threshold: 99.5, // 99.5% duration: 900000, // 15 minutes severity: 'critical', channels: ['slack', 'email', 'pagerduty', 'sms'], enabled: true } ]; defaultAlerts.forEach(alert => { this.alerts.set(alert.id, alert); }); } /** * Initialize health check functions */ initializeHealthChecks() { // CLI health check this.healthChecks.set('cli', async () => { try { // Check if CLI is responding const startTime = Date.now(); // This would make an actual health check request // For now, we'll simulate await new Promise(resolve => setTimeout(resolve, 100)); const responseTime = Date.now() - startTime; return { status: 'healthy', uptime: this.calculateUptime('cli'), responseTime, errorRate: this.calculateErrorRate('cli'), lastCheck: Date.now(), issues: [] }; } catch (error) { return { status: 'critical', uptime: 0, responseTime: -1, errorRate: 100, lastCheck: Date.now(), issues: [error instanceof Error ? error.message : 'Unknown error'] }; } }); // Web platform health check this.healthChecks.set('web', async () => { try { const startTime = Date.now(); // Check web platform health endpoint const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const response = await fetch('https://web.recoder.xyz/api/health', { method: 'GET', signal: controller.signal }); clearTimeout(timeoutId); const responseTime = Date.now() - startTime; if (response.ok) { return { status: 'healthy', uptime: this.calculateUptime('web'), responseTime, errorRate: this.calculateErrorRate('web'), lastCheck: Date.now(), issues: [] }; } else { return { status: 'degraded', uptime: this.calculateUptime('web'), responseTime, errorRate: this.calculateErrorRate('web'), lastCheck: Date.now(), issues: [`HTTP ${response.status}: ${response.statusText}`] }; } } catch (error) { return { status: 'critical', uptime: 0, responseTime: -1, errorRate: 100, lastCheck: Date.now(), issues: [error instanceof Error ? error.message : 'Network error'] }; } }); // AI providers health checks const aiProviders = ['claude', 'groq', 'gemini', 'ollama']; aiProviders.forEach(provider => { this.healthChecks.set(`ai:${provider}`, async () => { return this.checkAIProviderHealth(provider); }); }); } /** * Check AI provider health */ async checkAIProviderHealth(provider) { try { const startTime = Date.now(); // Make a simple test request to the AI provider let isHealthy = false; let responseTime = 0; switch (provider) { case 'claude': // Test Claude API if (process.env.ANTHROPIC_API_KEY) { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.ANTHROPIC_API_KEY, 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model: 'claude-3-5-haiku-20241022', max_tokens: 1, messages: [{ role: 'user', content: 'ping' }] }) }); isHealthy = response.ok; responseTime = Date.now() - startTime; } break; case 'groq': // Test Groq API if (process.env.GROQ_API_KEY) { const response = await fetch('https://api.groq.com/openai/v1/models', { headers: { 'Authorization': `Bearer ${process.env.GROQ_API_KEY}` } }); isHealthy = response.ok; responseTime = Date.now() - startTime; } break; case 'ollama': // Test Ollama if (process.env.OLLAMA_BASE_URL) { const response = await fetch(`${process.env.OLLAMA_BASE_URL}/api/tags`); isHealthy = response.ok; responseTime = Date.now() - startTime; } break; default: isHealthy = false; } return { status: isHealthy ? 'healthy' : 'degraded', uptime: this.calculateUptime(`ai:${provider}`), responseTime, errorRate: this.calculateErrorRate(`ai:${provider}`), lastCheck: Date.now(), issues: isHealthy ? [] : ['API not responding or not configured'] }; } catch (error) { return { status: 'critical', uptime: 0, responseTime: -1, errorRate: 100, lastCheck: Date.now(), issues: [error instanceof Error ? error.message : 'Connection failed'] }; } } /** * Perform health checks */ async performHealthChecks() { const healthPromises = Array.from(this.healthChecks.entries()).map(async ([service, check]) => { try { const health = await check(); this.emit('health:checked', service, health); return { service, health }; } catch (error) { const errorHealth = { status: 'critical', uptime: 0, responseTime: -1, errorRate: 100, lastCheck: Date.now(), issues: [error instanceof Error ? error.message : 'Health check failed'] }; this.emit('health:checked', service, errorHealth); return { service, health: errorHealth }; } }); const results = await Promise.all(healthPromises); // Calculate overall system health const systemHealth = this.calculateSystemHealth(results); this.emit('system:health', systemHealth); } /** * Calculate overall system health */ calculateSystemHealth(healthResults) { const services = { cli: healthResults.find(r => r.service === 'cli')?.health || this.getDefaultHealth(), web: healthResults.find(r => r.service === 'web')?.health || this.getDefaultHealth(), extension: this.getDefaultHealth(), // Extension health would be reported separately aiProviders: {} }; // Add AI provider health healthResults .filter(r => r.service.startsWith('ai:')) .forEach(r => { const provider = r.service.replace('ai:', ''); services.aiProviders[provider] = r.health; }); // Calculate overall status const allStatuses = [ services.cli.status, services.web.status, ...Object.values(services.aiProviders).map(h => h.status) ]; let overall = 'healthy'; if (allStatuses.some(s => s === 'critical')) { overall = 'critical'; } else if (allStatuses.some(s => s === 'degraded')) { overall = 'degraded'; } return { overall, services, metrics: { uptime: this.calculateAverageUptime(), responseTime: this.calculateAverageResponseTime(), errorRate: this.calculateAverageErrorRate(), activeUsers: this.getActiveUsers(), codeGenerations: this.getCodeGenerations() }, lastUpdated: Date.now() }; } /** * Start metric collection */ startMetricCollection() { // Collect system metrics every minute setInterval(() => { this.collectSystemMetrics(); }, 60000); // Collect usage metrics every 5 minutes setInterval(() => { this.collectUsageMetrics(); }, 300000); } /** * Collect system metrics */ collectSystemMetrics() { // CPU usage const cpuUsage = process.cpuUsage(); this.recordMetric({ name: 'cpu_usage', value: (cpuUsage.user + cpuUsage.system) / 1000000, // Convert to seconds unit: 'seconds', platform: 'cli' }); // Memory usage const memUsage = process.memoryUsage(); this.recordMetric({ name: 'memory_usage', value: memUsage.heapUsed / 1024 / 1024, // Convert to MB unit: 'MB', platform: 'cli' }); // Process uptime this.recordMetric({ name: 'process_uptime', value: process.uptime(), unit: 'seconds', platform: 'cli' }); } /** * Collect usage metrics */ collectUsageMetrics() { // These would be collected from actual usage data this.recordMetric({ name: 'active_users', value: this.getActiveUsers(), unit: 'count', platform: 'web' }); this.recordMetric({ name: 'code_generations', value: this.getCodeGenerations(), unit: 'count', platform: 'cli' }); } /** * Check alerts for a metric */ checkAlerts(metric) { for (const alert of this.alerts.values()) { if (!alert.enabled) continue; if (alert.metric === metric.name) { const shouldAlert = this.evaluateAlertCondition(alert, metric.value); if (shouldAlert) { this.triggerAlert(alert, metric); } } } } /** * Evaluate alert condition */ evaluateAlertCondition(alert, value) { switch (alert.condition) { case 'gt': return value > alert.threshold; case 'gte': return value >= alert.threshold; case 'lt': return value < alert.threshold; case 'lte': return value <= alert.threshold; case 'eq': return value === alert.threshold; default: return false; } } /** * Trigger alert */ triggerAlert(alert, metric) { const alertEvent = { alert, metric, timestamp: Date.now(), message: `Alert: ${alert.name} - ${metric.name} is ${metric.value} ${metric.unit} (threshold: ${alert.threshold})` }; this.emit('alert:triggered', alertEvent); // Send to alert channels alert.channels.forEach(channel => { this.sendAlert(channel, alertEvent); }); } /** * Send alert to channel */ sendAlert(channel, alertEvent) { switch (channel) { case 'slack': this.sendSlackAlert(alertEvent); break; case 'email': this.sendEmailAlert(alertEvent); break; case 'pagerduty': this.sendPagerDutyAlert(alertEvent); break; case 'sms': this.sendSMSAlert(alertEvent); break; default: console.warn(`Unknown alert channel: ${channel}`); } } /** * Send alert to Slack */ async sendSlackAlert(alertEvent) { if (!process.env.SLACK_WEBHOOK_URL) return; try { const payload = { text: `🚨 Recoder.xyz Alert`, attachments: [ { color: this.getAlertColor(alertEvent.alert.severity), fields: [ { title: 'Alert', value: alertEvent.alert.name, short: true }, { title: 'Metric', value: `${alertEvent.metric.name}: ${alertEvent.metric.value} ${alertEvent.metric.unit}`, short: true }, { title: 'Threshold', value: `${alertEvent.alert.condition} ${alertEvent.alert.threshold}`, short: true }, { title: 'Platform', value: alertEvent.metric.platform, short: true } ], ts: Math.floor(alertEvent.timestamp / 1000) } ] }; await fetch(process.env.SLACK_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); } catch (error) { console.error('Failed to send Slack alert:', error); } } /** * Send to external monitoring services */ sendToExternalServices(metric) { // Send to DataDog this.sendToDataDog(metric); // Send to New Relic this.sendToNewRelic(metric); // Send to Prometheus this.sendToPrometheus(metric); } /** * Send metric to DataDog */ async sendToDataDog(metric) { if (!process.env.DATADOG_API_KEY) return; try { const payload = { series: [ { metric: `recoder.${metric.platform}.${metric.name}`, points: [[Math.floor(metric.timestamp / 1000), metric.value]], tags: [ `platform:${metric.platform}`, ...(metric.dimensions ? Object.entries(metric.dimensions).map(([k, v]) => `${k}:${v}`) : []) ] } ] }; await fetch('https://api.datadoghq.com/api/v1/series', { method: 'POST', headers: { 'Content-Type': 'application/json', 'DD-API-KEY': process.env.DATADOG_API_KEY }, body: JSON.stringify(payload) }); } catch (error) { console.error('Failed to send metric to DataDog:', error); } } // Helper methods getAlertColor(severity) { switch (severity) { case 'critical': return 'danger'; case 'high': return 'warning'; case 'medium': return '#ff9900'; case 'low': return 'good'; default: return '#cccccc'; } } calculateUptime(service) { // Calculate uptime percentage based on historical data return 99.5; // Placeholder } calculateErrorRate(service) { // Calculate error rate based on historical data return 0.1; // Placeholder } calculateAverageUptime() { return 99.5; // Placeholder } calculateAverageResponseTime() { return 250; // Placeholder } calculateAverageErrorRate() { return 0.1; // Placeholder } getActiveUsers() { return 1247; // Placeholder - would come from actual analytics } getCodeGenerations() { return 3521; // Placeholder - would come from actual usage data } getDefaultHealth() { return { status: 'healthy', uptime: 99.5, responseTime: 200, errorRate: 0.1, lastCheck: Date.now(), issues: [] }; } async sendEmailAlert(alertEvent) { // Email alert implementation } async sendPagerDutyAlert(alertEvent) { // PagerDuty alert implementation } async sendSMSAlert(alertEvent) { // SMS alert implementation } async sendToNewRelic(metric) { // New Relic integration } async sendToPrometheus(metric) { // Prometheus integration } } exports.ProductionMonitoring = ProductionMonitoring; // Export singleton instance exports.productionMonitoring = ProductionMonitoring.getInstance(); exports.default = ProductionMonitoring; //# sourceMappingURL=production-monitoring.js.map