@executeautomation/playwright-mcp-server
Version:
Model Context Protocol servers for Playwright
215 lines (214 loc) • 7.93 kB
JavaScript
import express from 'express';
/**
* System Monitoring Class
* Tracks performance metrics and system health
*/
export class MonitoringSystem {
constructor(config) {
this.requestHistory = [];
this.config = config;
this.startTime = Date.now();
this.metrics = {
requestCount: 0,
errorCount: 0,
averageResponseTime: 0,
activeBrowsers: 0,
memoryUsage: 0,
uptime: 0,
activeConnections: 0,
rateLimitViolations: 0
};
}
/**
* Record a request for metrics tracking
* @param duration Request duration in milliseconds
* @param success Whether the request was successful
* @param category Optional request category
*/
recordRequest(duration, success, category) {
const requestMetric = {
duration,
success,
timestamp: Date.now(),
category
};
this.requestHistory.push(requestMetric);
this.metrics.requestCount++;
if (!success) {
this.metrics.errorCount++;
}
// Keep only recent requests for average calculation (last 1000)
if (this.requestHistory.length > 1000) {
this.requestHistory = this.requestHistory.slice(-1000);
}
this.updateAverageResponseTime();
}
/**
* Record a rate limit violation
*/
recordRateLimitViolation() {
this.metrics.rateLimitViolations++;
}
/**
* Update active browser count
* @param count Current number of active browsers
*/
updateActiveBrowsers(count) {
this.metrics.activeBrowsers = count;
}
/**
* Update active connections count
* @param count Current number of active SSE connections
*/
updateActiveConnections(count) {
this.metrics.activeConnections = count;
}
/**
* Get current system metrics
* @returns Current metrics snapshot
*/
getMetrics() {
this.updateSystemMetrics();
return { ...this.metrics };
}
/**
* Get system health status
* @returns Current health status
*/
getHealthStatus() {
const checks = {};
// Memory check
const memoryUsage = process.memoryUsage();
const memoryUsagePercent = (memoryUsage.heapUsed / memoryUsage.heapTotal) * 100;
checks.memory = {
status: memoryUsagePercent > this.config.memoryThreshold ? 'warn' : 'pass',
description: `Memory usage: ${memoryUsagePercent.toFixed(2)}%`,
data: { memoryUsage, threshold: this.config.memoryThreshold }
};
// Response time check
const avgResponseTime = this.metrics.averageResponseTime;
checks.responseTime = {
status: avgResponseTime > this.config.responseTimeThreshold ? 'warn' : 'pass',
description: `Average response time: ${avgResponseTime.toFixed(2)}ms`,
data: { averageResponseTime: avgResponseTime, threshold: this.config.responseTimeThreshold }
};
// Error rate check
const errorRate = this.metrics.requestCount > 0 ? (this.metrics.errorCount / this.metrics.requestCount) * 100 : 0;
checks.errorRate = {
status: errorRate > 10 ? 'warn' : errorRate > 5 ? 'warn' : 'pass',
description: `Error rate: ${errorRate.toFixed(2)}%`,
data: { errorRate, errorCount: this.metrics.errorCount, requestCount: this.metrics.requestCount }
};
// Overall status
const hasFailures = Object.values(checks).some(check => check.status === 'fail');
const hasWarnings = Object.values(checks).some(check => check.status === 'warn');
const status = hasFailures ? 'unhealthy' : hasWarnings ? 'degraded' : 'healthy';
return {
status,
checks,
timestamp: Date.now(),
version: process.env.npm_package_version
};
}
/**
* Start metrics collection and HTTP server for health checks
* @param port Optional port for HTTP server (default: 3001)
*/
async startMetricsCollection(port = 3001) {
if (!this.config.enabled)
return;
this.metricsInterval = setInterval(() => {
this.updateSystemMetrics();
}, this.config.metricsInterval);
this.healthCheckInterval = setInterval(() => {
// Health checks are performed on-demand via getHealthStatus()
// This interval can be used for proactive health monitoring
}, this.config.healthCheckInterval);
// Start HTTP server for health check endpoints
await this.startHttpServer(port);
}
/**
* Start HTTP server for health check endpoints
* @param port Port to listen on
*/
async startHttpServer(port) {
this.app = express();
this.app.use(express.json());
// Health check endpoint
this.app.get('/health', (req, res) => {
const healthStatus = this.getHealthStatus();
const statusCode = healthStatus.status === 'healthy' ? 200 :
healthStatus.status === 'degraded' ? 200 : 503;
res.status(statusCode).json(healthStatus);
});
// Metrics endpoint
this.app.get('/metrics', (req, res) => {
const metrics = this.getMetrics();
res.json(metrics);
});
// Ready endpoint (simple health check)
this.app.get('/ready', (req, res) => {
res.json({ status: 'ready', timestamp: Date.now() });
});
return new Promise((resolve, reject) => {
this.httpServer = this.app.listen(port, () => {
// Get the actual port assigned (important when using port 0 for dynamic allocation)
const address = this.httpServer.address();
this.actualPort = typeof address === 'object' && address !== null ? address.port : port;
console.log(`Monitoring HTTP server listening on port ${this.actualPort}`);
console.log(`Health check: http://localhost:${this.actualPort}/health`);
console.log(`Metrics: http://localhost:${this.actualPort}/metrics`);
resolve();
});
this.httpServer.on('error', (error) => {
reject(error);
});
});
}
/**
* Stop metrics collection and HTTP server
*/
async stopMetricsCollection() {
if (this.metricsInterval) {
clearInterval(this.metricsInterval);
this.metricsInterval = undefined;
}
if (this.healthCheckInterval) {
clearInterval(this.healthCheckInterval);
this.healthCheckInterval = undefined;
}
if (this.httpServer) {
return new Promise((resolve) => {
this.httpServer.close(() => {
console.log('Monitoring HTTP server stopped');
resolve();
});
});
}
}
/**
* Update system-level metrics
*/
updateSystemMetrics() {
this.metrics.uptime = Date.now() - this.startTime;
this.metrics.memoryUsage = process.memoryUsage().heapUsed;
}
/**
* Update average response time based on recent requests
*/
updateAverageResponseTime() {
if (this.requestHistory.length === 0) {
this.metrics.averageResponseTime = 0;
return;
}
const totalTime = this.requestHistory.reduce((sum, req) => sum + req.duration, 0);
this.metrics.averageResponseTime = totalTime / this.requestHistory.length;
}
/**
* Get the actual port the monitoring server is listening on
* @returns The port number, or undefined if server not started
*/
getMonitoringPort() {
return this.actualPort;
}
}