UNPKG

@j03fr0st/pubg-ts

Version:

A comprehensive TypeScript wrapper for the PUBG API

375 lines 13.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.monitoringSystem = exports.MonitoringSystem = void 0; const node_perf_hooks_1 = require("node:perf_hooks"); const prom_client_1 = __importStar(require("prom-client")); const api_1 = require("@opentelemetry/api"); const logger_1 = require("./logger"); /** * Production-ready monitoring and observability system * * Provides comprehensive monitoring capabilities including: * - Prometheus metrics collection * - OpenTelemetry distributed tracing * - Health checks and system monitoring * - Performance tracking and alerting * * @example * ```typescript * const monitor = new MonitoringSystem({ * enabled: true, * metricsEnabled: true, * tracingEnabled: true, * prefix: 'pubg_sdk' * }); * * // Track API requests * const span = monitor.startSpan('api_request', { endpoint: '/players' }); * try { * const result = await apiCall(); * monitor.recordRequestMetrics({ * duration: 150, * statusCode: 200, * endpoint: '/players' * }); * span.setStatus({ code: SpanStatusCode.OK }); * } catch (error) { * monitor.recordError(error); * span.recordException(error as Error); * span.setStatus({ code: SpanStatusCode.ERROR }); * } finally { * span.end(); * } * ``` */ class MonitoringSystem { constructor(config = {}) { // Health tracking this.healthStatus = 'healthy'; this.lastHealthCheck = 0; this.config = { enabled: true, metricsEnabled: true, tracingEnabled: true, healthCheckEnabled: true, collectDefaultMetrics: true, prefix: 'pubg_sdk', ...config }; this.startTime = Date.now(); this.registry = new prom_client_1.Registry(); if (this.config.enabled) { this.initializeMetrics(); this.initializeTracing(); this.startHealthChecking(); if (this.config.collectDefaultMetrics) { prom_client_1.default.collectDefaultMetrics({ register: this.registry, prefix: `${this.config.prefix}_` }); } } } initializeMetrics() { if (!this.config.metricsEnabled) return; const prefix = this.config.prefix; // Request metrics this.requestCounter = new prom_client_1.Counter({ name: `${prefix}_requests_total`, help: 'Total number of API requests', labelNames: ['method', 'endpoint', 'status_code', 'status_class'], registers: [this.registry] }); this.requestDuration = new prom_client_1.Histogram({ name: `${prefix}_request_duration_seconds`, help: 'Request duration in seconds', labelNames: ['method', 'endpoint', 'status_code'], buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2, 5, 10], registers: [this.registry] }); this.errorCounter = new prom_client_1.Counter({ name: `${prefix}_errors_total`, help: 'Total number of errors', labelNames: ['type', 'endpoint', 'error_code'], registers: [this.registry] }); // System metrics this.cacheHitRate = new prom_client_1.Gauge({ name: `${prefix}_cache_hit_rate`, help: 'Cache hit rate percentage', registers: [this.registry] }); this.activeConnections = new prom_client_1.Gauge({ name: `${prefix}_active_connections`, help: 'Number of active connections', registers: [this.registry] }); this.rateLimitRemaining = new prom_client_1.Gauge({ name: `${prefix}_rate_limit_remaining`, help: 'Remaining rate limit quota', registers: [this.registry] }); this.memoryUsage = new prom_client_1.Gauge({ name: `${prefix}_memory_usage_bytes`, help: 'Memory usage in bytes', labelNames: ['type'], registers: [this.registry] }); logger_1.logger.client('Prometheus metrics initialized'); } initializeTracing() { if (!this.config.tracingEnabled) return; this.tracer = api_1.trace.getTracer('pubg-ts-sdk', '1.0.0'); logger_1.logger.client('OpenTelemetry tracing initialized'); } startHealthChecking() { if (!this.config.healthCheckEnabled) return; // Run health checks every 30 seconds setInterval(() => { this.performHealthCheck(); }, 30000); // Initial health check this.performHealthCheck(); } /** * Start a new tracing span */ startSpan(name, attributes = {}) { if (!this.config.tracingEnabled || !this.tracer) { return { end: () => { }, setStatus: () => { }, recordException: () => { } }; } return this.tracer.startSpan(name, { kind: api_1.SpanKind.CLIENT, attributes }); } /** * Record request metrics */ recordRequestMetrics(metrics) { if (!this.config.metricsEnabled) return; const { duration, statusCode = 0, endpoint = 'unknown', method = 'unknown', error = false } = metrics; const statusClass = this.getStatusClass(statusCode); // Increment request counter this.requestCounter.inc({ method, endpoint, status_code: statusCode.toString(), status_class: statusClass }); // Record request duration this.requestDuration.observe({ method, endpoint, status_code: statusCode.toString() }, duration / 1000 // Convert to seconds ); // Record errors if (error || statusCode >= 400) { this.errorCounter.inc({ type: error ? 'client_error' : 'http_error', endpoint, error_code: statusCode.toString() }); } } /** * Record error occurrences */ recordError(error, context = {}) { if (!this.config.metricsEnabled) return; this.errorCounter.inc({ type: error.constructor.name, endpoint: context.endpoint || 'unknown', error_code: context.code || 'unknown' }); logger_1.logger.error('Error recorded in monitoring', { error: error.message, context }); } /** * Update cache metrics */ updateCacheMetrics(hitRate) { if (!this.config.metricsEnabled) return; this.cacheHitRate.set(hitRate); } /** * Update rate limit metrics */ updateRateLimitMetrics(remaining) { if (!this.config.metricsEnabled) return; this.rateLimitRemaining.set(remaining); } /** * Update connection metrics */ updateConnectionMetrics(active) { if (!this.config.metricsEnabled) return; this.activeConnections.set(active); } /** * Get current system health */ async getHealth() { const now = Date.now(); const memStats = process.memoryUsage(); return { status: this.healthStatus, timestamp: now, uptime: now - this.startTime, memory: { used: memStats.heapUsed, total: memStats.heapTotal, percentage: (memStats.heapUsed / memStats.heapTotal) * 100 }, api: { status: this.healthStatus, responseTime: await this.measureApiResponseTime(), errorRate: this.calculateErrorRate() }, cache: { hitRate: this.getCacheHitRate(), size: 0, // Would be set by cache implementation maxSize: 100 // Would be set by cache implementation }, rateLimit: { remaining: this.getRateLimitRemaining(), limit: 10, // Would be set by rate limiter resetTime: Date.now() + 60000 } }; } /** * Get Prometheus metrics */ async getMetrics() { if (!this.config.metricsEnabled) { return '# Metrics disabled\n'; } // Update memory metrics before collecting const memStats = process.memoryUsage(); this.memoryUsage.set({ type: 'heap_used' }, memStats.heapUsed); this.memoryUsage.set({ type: 'heap_total' }, memStats.heapTotal); this.memoryUsage.set({ type: 'external' }, memStats.external); this.memoryUsage.set({ type: 'rss' }, memStats.rss); return await this.registry.metrics(); } /** * Perform comprehensive health check */ async performHealthCheck() { const startTime = node_perf_hooks_1.performance.now(); try { // Check memory usage const memStats = process.memoryUsage(); const memoryPercentage = (memStats.heapUsed / memStats.heapTotal) * 100; // Check API response time const apiResponseTime = await this.measureApiResponseTime(); // Calculate error rate const errorRate = this.calculateErrorRate(); // Determine overall health status let status = 'healthy'; if (memoryPercentage > 90 || apiResponseTime > 5000 || errorRate > 0.1) { status = 'unhealthy'; } else if (memoryPercentage > 70 || apiResponseTime > 2000 || errorRate > 0.05) { status = 'degraded'; } this.healthStatus = status; this.lastHealthCheck = Date.now(); const duration = node_perf_hooks_1.performance.now() - startTime; logger_1.logger.client('Health check completed', { status, duration: `${duration.toFixed(2)}ms`, memory: `${memoryPercentage.toFixed(2)}%`, apiResponseTime: `${apiResponseTime}ms`, errorRate: `${(errorRate * 100).toFixed(2)}%` }); } catch (error) { this.healthStatus = 'unhealthy'; logger_1.logger.error('Health check failed', { error }); } } async measureApiResponseTime() { // In a real implementation, this would make a test API call // For now, return a simulated response time return Math.random() * 500 + 100; // 100-600ms } calculateErrorRate() { // In a real implementation, this would calculate from actual metrics // For now, return a simulated error rate return Math.random() * 0.01; // 0-1% } getCacheHitRate() { // Would be calculated from actual cache statistics return 0.85; // 85% hit rate } getRateLimitRemaining() { // Would be retrieved from actual rate limiter return 8; // 8 requests remaining } getStatusClass(statusCode) { if (statusCode >= 200 && statusCode < 300) return '2xx'; if (statusCode >= 300 && statusCode < 400) return '3xx'; if (statusCode >= 400 && statusCode < 500) return '4xx'; if (statusCode >= 500) return '5xx'; return 'unknown'; } /** * Shutdown monitoring system gracefully */ shutdown() { logger_1.logger.client('Shutting down monitoring system'); this.registry.clear(); } } exports.MonitoringSystem = MonitoringSystem; // Export singleton instance exports.monitoringSystem = new MonitoringSystem({ enabled: process.env.MONITORING_ENABLED !== 'false', metricsEnabled: process.env.METRICS_ENABLED !== 'false', tracingEnabled: process.env.TRACING_ENABLED !== 'false', prefix: process.env.METRICS_PREFIX || 'pubg_sdk' }); //# sourceMappingURL=monitoring.js.map