UNPKG

failover-sdk

Version:

One-line API failover with zero downtime. Native Rust performance with TypeScript interface.

298 lines 11.9 kB
"use strict"; // Metrics Manager - Pure JavaScript implementation // Adapted from failover-core Rust implementation, no native dependencies Object.defineProperty(exports, "__esModule", { value: true }); exports.MetricsManager = void 0; exports.initialize = initialize; exports.trackRequest = trackRequest; exports.trackFailover = trackFailover; exports.getSummary = getSummary; exports.getProviderMetrics = getProviderMetrics; exports.exportPrometheus = exportPrometheus; exports.createExpressMiddleware = createExpressMiddleware; class MetricsStore { constructor() { this.MAX_RECENT_REQUESTS = 1000; this.MAX_FAILOVER_EVENTS = 100; this.start_time = Date.now(); this.provider_metrics = new Map(); this.recent_requests = []; this.failover_events = []; this.request_counts = new Map(); } trackRequest(provider, service, operation, method, status_code, response_time_ms) { const timestamp = Date.now(); const success = status_code >= 200 && status_code < 400; // Update provider metrics if (!this.provider_metrics.has(provider)) { this.provider_metrics.set(provider, { provider, requests: 0, successes: 0, failures: 0, average_response_time_ms: 0, total_response_time_ms: 0, last_request_time: timestamp, health_score: 1.0, }); } const provider_stats = this.provider_metrics.get(provider); provider_stats.requests++; provider_stats.total_response_time_ms += response_time_ms; provider_stats.average_response_time_ms = provider_stats.total_response_time_ms / provider_stats.requests; provider_stats.last_request_time = timestamp; if (success) { provider_stats.successes++; } else { provider_stats.failures++; } // Update health score provider_stats.health_score = provider_stats.successes / provider_stats.requests; // Track request const request = { timestamp, provider, service, operation, method, status_code, response_time_ms, success, }; this.recent_requests.push(request); if (this.recent_requests.length > this.MAX_RECENT_REQUESTS) { this.recent_requests = this.recent_requests.slice(-this.MAX_RECENT_REQUESTS); } // Update request counts for RPS calculation const minute_key = Math.floor(timestamp / 60000); // Group by minute this.request_counts.set(minute_key.toString(), (this.request_counts.get(minute_key.toString()) || 0) + 1); } trackFailover(from_provider, to_provider, reason, service, operation) { const event = { timestamp: Date.now(), from_provider, to_provider, reason, service, operation, }; this.failover_events.push(event); if (this.failover_events.length > this.MAX_FAILOVER_EVENTS) { this.failover_events = this.failover_events.slice(-this.MAX_FAILOVER_EVENTS); } } getSummary() { const now = Date.now(); const uptime_seconds = (now - this.start_time) / 1000; let total_requests = 0; let total_failures = 0; let total_response_time = 0; for (const metrics of this.provider_metrics.values()) { total_requests += metrics.requests; total_failures += metrics.failures; total_response_time += metrics.total_response_time_ms; } const average_response_time_ms = total_requests > 0 ? total_response_time / total_requests : 0; // Calculate requests per second (last minute) const current_minute = Math.floor(now / 60000); const requests_last_minute = this.request_counts.get(current_minute.toString()) || 0; const requests_per_second = requests_last_minute / 60; return { total_requests, total_failures, total_failovers: this.failover_events.length, active_connections: this.getActiveConnections(), uptime_seconds, average_response_time_ms, requests_per_second, }; } getProviderMetrics() { const metrics = {}; for (const [provider, stats] of this.provider_metrics) { metrics[provider] = { ...stats }; } return metrics; } getFailoverEvents() { return [...this.failover_events]; } getRecentRequests(limit) { const requests = [...this.recent_requests].reverse(); // Most recent first return limit ? requests.slice(0, limit) : requests; } getActiveConnections() { // Estimate active connections based on recent activity const now = Date.now(); const five_minutes_ago = now - 5 * 60 * 1000; const recent_providers = new Set(); for (const request of this.recent_requests) { if (request.timestamp > five_minutes_ago) { recent_providers.add(request.provider); } } return recent_providers.size; } exportPrometheus() { const metrics = []; const timestamp = Date.now(); // Global metrics const summary = this.getSummary(); metrics.push(`# HELP failover_requests_total Total number of requests processed`); metrics.push(`# TYPE failover_requests_total counter`); metrics.push(`failover_requests_total ${summary.total_requests} ${timestamp}`); metrics.push(`# HELP failover_failures_total Total number of failed requests`); metrics.push(`# TYPE failover_failures_total counter`); metrics.push(`failover_failures_total ${summary.total_failures} ${timestamp}`); metrics.push(`# HELP failover_events_total Total number of failover events`); metrics.push(`# TYPE failover_events_total counter`); metrics.push(`failover_events_total ${summary.total_failovers} ${timestamp}`); metrics.push(`# HELP failover_uptime_seconds Service uptime in seconds`); metrics.push(`# TYPE failover_uptime_seconds gauge`); metrics.push(`failover_uptime_seconds ${summary.uptime_seconds} ${timestamp}`); metrics.push(`# HELP failover_response_time_ms Average response time in milliseconds`); metrics.push(`# TYPE failover_response_time_ms gauge`); metrics.push(`failover_response_time_ms ${summary.average_response_time_ms} ${timestamp}`); metrics.push(`# HELP failover_requests_per_second Current requests per second`); metrics.push(`# TYPE failover_requests_per_second gauge`); metrics.push(`failover_requests_per_second ${summary.requests_per_second} ${timestamp}`); // Provider-specific metrics metrics.push(`# HELP failover_provider_requests_total Requests per provider`); metrics.push(`# TYPE failover_provider_requests_total counter`); metrics.push(`# HELP failover_provider_response_time_ms Average response time per provider`); metrics.push(`# TYPE failover_provider_response_time_ms gauge`); metrics.push(`# HELP failover_provider_health_score Health score per provider (0-1)`); metrics.push(`# TYPE failover_provider_health_score gauge`); for (const [provider, stats] of this.provider_metrics) { metrics.push(`failover_provider_requests_total{provider="${provider}"} ${stats.requests} ${timestamp}`); metrics.push(`failover_provider_response_time_ms{provider="${provider}"} ${stats.average_response_time_ms} ${timestamp}`); metrics.push(`failover_provider_health_score{provider="${provider}"} ${stats.health_score} ${timestamp}`); } return metrics.join('\n') + '\n'; } reset() { this.start_time = Date.now(); this.provider_metrics.clear(); this.recent_requests = []; this.failover_events = []; this.request_counts.clear(); } } // Global metrics store const globalMetricsStore = new MetricsStore(); class MetricsManager { /** * Initialize the metrics system (call once at startup) */ static initialize() { if (!this.initialized) { this.initialized = true; console.log('[Metrics] Metrics system initialized'); } } /** * Track a request */ static trackRequest(provider, service, operation, method, status_code, response_time_ms) { if (!this.initialized) this.initialize(); globalMetricsStore.trackRequest(provider, service, operation, method, status_code, response_time_ms); } /** * Track a failover event */ static trackFailover(from_provider, to_provider, reason, service = 'unknown', operation = 'unknown') { if (!this.initialized) this.initialize(); globalMetricsStore.trackFailover(from_provider, to_provider, reason, service, operation); } /** * Get metrics summary */ static getSummary() { if (!this.initialized) this.initialize(); return globalMetricsStore.getSummary(); } /** * Get provider-specific metrics */ static getProviderMetrics() { if (!this.initialized) this.initialize(); return globalMetricsStore.getProviderMetrics(); } /** * Get recent failover events */ static getFailoverEvents() { if (!this.initialized) this.initialize(); return globalMetricsStore.getFailoverEvents(); } /** * Get recent requests */ static getRecentRequests(limit) { if (!this.initialized) this.initialize(); return globalMetricsStore.getRecentRequests(limit); } /** * Export metrics in Prometheus format */ static exportPrometheus() { if (!this.initialized) this.initialize(); return globalMetricsStore.exportPrometheus(); } /** * Reset all metrics */ static reset() { globalMetricsStore.reset(); } /** * Create Express.js middleware for automatic request tracking */ static createExpressMiddleware() { return (req, res, next) => { const start_time = Date.now(); res.on('finish', () => { const response_time = Date.now() - start_time; const provider = res.get('X-Failover-Provider') || 'unknown'; const service = res.get('X-Failover-Service') || 'api'; const operation = res.get('X-Failover-Operation') || req.path; this.trackRequest(provider, service, operation, req.method, res.statusCode, response_time); }); next(); }; } } exports.MetricsManager = MetricsManager; MetricsManager.initialized = false; // Convenience functions (match the failover-js API) function initialize() { MetricsManager.initialize(); } function trackRequest(provider, service, operation, method, status_code, response_time_ms) { MetricsManager.trackRequest(provider, service, operation, method, status_code, response_time_ms); } function trackFailover(from_provider, to_provider, reason) { MetricsManager.trackFailover(from_provider, to_provider, reason); } function getSummary() { return MetricsManager.getSummary(); } function getProviderMetrics() { return MetricsManager.getProviderMetrics(); } function exportPrometheus() { return MetricsManager.exportPrometheus(); } function createExpressMiddleware() { return MetricsManager.createExpressMiddleware(); } //# sourceMappingURL=metrics.js.map