@gebrai/gebrai
Version:
Model Context Protocol server for GeoGebra mathematical visualization
161 lines • 5.91 kB
JavaScript
;
/**
* Performance Monitoring and Optimization System
* GEB-9: Performance Optimization: Response Time and Resource Management
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.performanceMonitor = exports.PerformanceMonitor = void 0;
class PerformanceMonitor {
static instance;
metrics = [];
thresholds;
maxMetrics = 1000; // Keep last 1000 metrics
// PRD requirements: <2 second response time
defaultThresholds = {
'geogebra_eval_command': { warning: 1000, critical: 2000 },
'geogebra_create_point': { warning: 500, critical: 1000 },
'geogebra_create_line': { warning: 500, critical: 1000 },
'geogebra_export_png': { warning: 1500, critical: 2000 },
'geogebra_export_svg': { warning: 800, critical: 1500 },
'geogebra_instance_init': { warning: 8000, critical: 15000 },
'geogebra_clear_construction': { warning: 300, critical: 1000 },
'default': { warning: 1000, critical: 2000 }
};
constructor() {
this.thresholds = { ...this.defaultThresholds };
}
static getInstance() {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor();
}
return PerformanceMonitor.instance;
}
/**
* Start timing an operation
*/
startTiming(operationName, metadata) {
const startTime = performance.now();
const startMemory = process.memoryUsage();
return (success = true, errorMessage) => {
const endTime = performance.now();
const endMemory = process.memoryUsage();
const duration = endTime - startTime;
const metric = {
operationName,
startTime,
endTime,
duration,
success,
...(errorMessage && { errorMessage }),
memoryUsage: {
rss: endMemory.rss - startMemory.rss,
heapTotal: endMemory.heapTotal - startMemory.heapTotal,
heapUsed: endMemory.heapUsed - startMemory.heapUsed,
external: endMemory.external - startMemory.external,
arrayBuffers: endMemory.arrayBuffers - startMemory.arrayBuffers
},
...(metadata && { metadata })
};
this.addMetric(metric);
this.checkThresholds(metric);
return metric;
};
}
/**
* Measure and record an async operation
*/
async measureOperation(operationName, operation, metadata) {
const endTiming = this.startTiming(operationName, metadata);
try {
const result = await operation();
endTiming(true);
return result;
}
catch (error) {
endTiming(false, error instanceof Error ? error.message : String(error));
throw error;
}
}
/**
* Add a metric to the collection
*/
addMetric(metric) {
this.metrics.push(metric);
// Keep only the last maxMetrics entries
if (this.metrics.length > this.maxMetrics) {
this.metrics = this.metrics.slice(-this.maxMetrics);
}
}
/**
* Check if metric exceeds thresholds and log warnings
*/
checkThresholds(metric) {
const threshold = this.thresholds[metric.operationName] || this.thresholds['default'];
if (threshold && metric.duration > threshold.critical) {
console.error(`🚨 CRITICAL: ${metric.operationName} took ${metric.duration.toFixed(2)}ms (threshold: ${threshold.critical}ms)`);
}
else if (threshold && metric.duration > threshold.warning) {
console.warn(`⚠️ WARNING: ${metric.operationName} took ${metric.duration.toFixed(2)}ms (threshold: ${threshold.warning}ms)`);
}
}
/**
* Get performance statistics for an operation
*/
getStats(operationName) {
const relevantMetrics = operationName
? this.metrics.filter(m => m.operationName === operationName)
: this.metrics;
if (relevantMetrics.length === 0) {
return {
count: 0,
averageDuration: 0,
medianDuration: 0,
minDuration: 0,
maxDuration: 0,
successRate: 0,
p95Duration: 0,
p99Duration: 0
};
}
const durations = relevantMetrics.map(m => m.duration).sort((a, b) => a - b);
const successCount = relevantMetrics.filter(m => m.success).length;
return {
count: relevantMetrics.length,
averageDuration: durations.reduce((sum, d) => sum + d, 0) / durations.length,
medianDuration: durations[Math.floor(durations.length / 2)] || 0,
minDuration: durations[0] || 0,
maxDuration: durations[durations.length - 1] || 0,
successRate: successCount / relevantMetrics.length,
p95Duration: durations[Math.floor(durations.length * 0.95)] || 0,
p99Duration: durations[Math.floor(durations.length * 0.99)] || 0
};
}
/**
* Get all operation names with metrics
*/
getOperationNames() {
return [...new Set(this.metrics.map(m => m.operationName))];
}
/**
* Clear all metrics
*/
clearMetrics() {
this.metrics = [];
}
/**
* Export metrics for analysis
*/
exportMetrics() {
return [...this.metrics];
}
/**
* Set custom thresholds
*/
setThresholds(thresholds) {
Object.assign(this.thresholds, thresholds);
}
}
exports.PerformanceMonitor = PerformanceMonitor;
// Global instance
exports.performanceMonitor = PerformanceMonitor.getInstance();
//# sourceMappingURL=index.js.map