@j03fr0st/pubg-ts
Version:
A comprehensive TypeScript wrapper for the PUBG API
375 lines • 13.4 kB
JavaScript
"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