failover-sdk
Version:
One-line API failover with zero downtime. Native Rust performance with TypeScript interface.
298 lines • 11.9 kB
JavaScript
;
// 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