il2cpp-dump-analyzer-mcp
Version:
Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games
381 lines • 13.1 kB
JavaScript
"use strict";
/**
* Metrics collection and export service for IL2CPP dump analyzer MCP system
* Provides Prometheus-compatible metrics export and monitoring dashboards
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetricsService = void 0;
const events_1 = require("events");
/**
* Metrics collection and export service
*/
class MetricsService extends events_1.EventEmitter {
constructor(config) {
super();
this.metrics = new Map();
this.isRunning = false;
// Built-in metric definitions
this.builtInMetrics = {
// System metrics
'il2cpp_memory_usage_bytes': {
type: 'gauge',
help: 'Memory usage in bytes'
},
'il2cpp_cpu_usage_percent': {
type: 'gauge',
help: 'CPU usage percentage'
},
'il2cpp_uptime_seconds': {
type: 'counter',
help: 'Application uptime in seconds'
},
// Database metrics
'il2cpp_database_connections_active': {
type: 'gauge',
help: 'Number of active database connections'
},
'il2cpp_database_queries_total': {
type: 'counter',
help: 'Total number of database queries'
},
'il2cpp_database_query_duration_seconds': {
type: 'histogram',
help: 'Database query duration in seconds'
},
'il2cpp_database_errors_total': {
type: 'counter',
help: 'Total number of database errors'
},
// Vector store metrics
'il2cpp_vector_store_documents_total': {
type: 'gauge',
help: 'Total number of documents in vector store'
},
'il2cpp_vector_store_search_duration_seconds': {
type: 'histogram',
help: 'Vector search duration in seconds'
},
'il2cpp_vector_store_cache_hits_total': {
type: 'counter',
help: 'Total number of cache hits'
},
'il2cpp_vector_store_cache_misses_total': {
type: 'counter',
help: 'Total number of cache misses'
},
// Embeddings metrics
'il2cpp_embeddings_generation_duration_seconds': {
type: 'histogram',
help: 'Embeddings generation duration in seconds'
},
'il2cpp_embeddings_cache_size_bytes': {
type: 'gauge',
help: 'Embeddings cache size in bytes'
},
// MCP server metrics
'il2cpp_mcp_requests_total': {
type: 'counter',
help: 'Total number of MCP requests'
},
'il2cpp_mcp_request_duration_seconds': {
type: 'histogram',
help: 'MCP request duration in seconds'
},
'il2cpp_mcp_errors_total': {
type: 'counter',
help: 'Total number of MCP errors'
},
'il2cpp_mcp_active_connections': {
type: 'gauge',
help: 'Number of active MCP connections'
},
// Health check metrics
'il2cpp_health_check_status': {
type: 'gauge',
help: 'Health check status (1=healthy, 0.5=degraded, 0=unhealthy)'
},
'il2cpp_health_check_duration_seconds': {
type: 'histogram',
help: 'Health check duration in seconds'
}
};
this.config = {
enabled: true,
exportInterval: 60000, // 1 minute
retentionPeriod: 3600000, // 1 hour
prometheusEnabled: true,
prometheusPort: 9090,
customMetrics: true,
...config
};
}
/**
* Initialize metrics service with health service integration
*/
initialize(healthService) {
this.healthService = healthService;
// Listen to health check events
this.healthService.on('healthCheck', (status) => {
this.recordHealthMetrics(status);
});
console.log('Metrics service initialized');
}
/**
* Start metrics collection
*/
start() {
if (!this.config.enabled || this.isRunning) {
return;
}
this.isRunning = true;
console.log(`Starting metrics collection with ${this.config.exportInterval}ms interval`);
// Start periodic metrics export
this.exportInterval = setInterval(() => {
this.exportMetrics();
this.cleanupOldMetrics();
}, this.config.exportInterval);
this.emit('started');
}
/**
* Stop metrics collection
*/
stop() {
if (!this.isRunning) {
return;
}
this.isRunning = false;
if (this.exportInterval) {
clearInterval(this.exportInterval);
this.exportInterval = undefined;
}
console.log('Metrics collection stopped');
this.emit('stopped');
}
/**
* Record a metric value
*/
recordMetric(metric) {
if (!this.config.enabled) {
return;
}
const metricWithTimestamp = {
...metric,
timestamp: new Date()
};
const existing = this.metrics.get(metric.name) || [];
existing.push(metricWithTimestamp);
this.metrics.set(metric.name, existing);
this.emit('metricRecorded', metricWithTimestamp);
}
/**
* Record health check metrics from health status
*/
recordHealthMetrics(status) {
// Overall health status
const healthValue = status.status === 'healthy' ? 1 :
status.status === 'degraded' ? 0.5 : 0;
this.recordMetric({
name: 'il2cpp_health_check_status',
value: healthValue,
type: 'gauge',
labels: { status: status.status }
});
// Uptime
this.recordMetric({
name: 'il2cpp_uptime_seconds',
value: status.uptime / 1000,
type: 'counter'
});
// Memory metrics
this.recordMetric({
name: 'il2cpp_memory_usage_bytes',
value: status.metrics.memory.used,
type: 'gauge',
labels: { type: 'rss' }
});
this.recordMetric({
name: 'il2cpp_memory_usage_bytes',
value: status.metrics.memory.heapUsed,
type: 'gauge',
labels: { type: 'heap_used' }
});
this.recordMetric({
name: 'il2cpp_memory_usage_bytes',
value: status.metrics.memory.heapTotal,
type: 'gauge',
labels: { type: 'heap_total' }
});
// CPU metrics
this.recordMetric({
name: 'il2cpp_cpu_usage_percent',
value: status.metrics.cpu.usage,
type: 'gauge'
});
// Database metrics
this.recordMetric({
name: 'il2cpp_database_connections_active',
value: status.metrics.database.connectionCount,
type: 'gauge'
});
// MCP metrics
this.recordMetric({
name: 'il2cpp_mcp_requests_total',
value: status.metrics.mcp.totalRequests,
type: 'counter'
});
this.recordMetric({
name: 'il2cpp_mcp_active_connections',
value: status.metrics.mcp.activeConnections,
type: 'gauge'
});
// Component health metrics
status.components.forEach(component => {
const componentHealthValue = component.status === 'healthy' ? 1 :
component.status === 'degraded' ? 0.5 : 0;
this.recordMetric({
name: 'il2cpp_component_health_status',
value: componentHealthValue,
type: 'gauge',
labels: {
component: component.name,
status: component.status
}
});
if (component.responseTime !== undefined) {
this.recordMetric({
name: 'il2cpp_component_response_time_seconds',
value: component.responseTime / 1000,
type: 'histogram',
labels: { component: component.name }
});
}
});
}
/**
* Get current metrics in Prometheus format
*/
getPrometheusMetrics() {
if (!this.config.prometheusEnabled) {
return '';
}
const prometheusMetrics = [];
// Group metrics by name
for (const [metricName, values] of this.metrics.entries()) {
if (values.length === 0)
continue;
const latestValues = this.getLatestMetricValues(values);
const metricDef = this.builtInMetrics[metricName];
prometheusMetrics.push({
name: metricName,
type: metricDef?.type || 'gauge',
help: metricDef?.help || `${metricName} metric`,
values: latestValues.map(v => ({
value: v.value,
labels: v.labels
}))
});
}
return this.formatPrometheusOutput(prometheusMetrics);
}
/**
* Get latest metric values (deduplicated by labels)
*/
getLatestMetricValues(values) {
const latestByLabels = new Map();
values.forEach(value => {
const labelKey = JSON.stringify(value.labels || {});
const existing = latestByLabels.get(labelKey);
if (!existing || value.timestamp > existing.timestamp) {
latestByLabels.set(labelKey, value);
}
});
return Array.from(latestByLabels.values());
}
/**
* Format metrics in Prometheus exposition format
*/
formatPrometheusOutput(metrics) {
let output = '';
metrics.forEach(metric => {
// Add metric metadata
output += `# HELP ${metric.name} ${metric.help}\n`;
output += `# TYPE ${metric.name} ${metric.type}\n`;
// Add metric values
metric.values.forEach(value => {
const labelsStr = value.labels ?
'{' + Object.entries(value.labels)
.map(([k, v]) => `${k}="${v}"`)
.join(',') + '}' : '';
output += `${metric.name}${labelsStr} ${value.value}\n`;
});
output += '\n';
});
return output;
}
/**
* Export metrics (can be extended for different export targets)
*/
exportMetrics() {
if (!this.config.enabled) {
return;
}
const prometheusMetrics = this.getPrometheusMetrics();
this.emit('metricsExported', {
format: 'prometheus',
data: prometheusMetrics,
timestamp: new Date()
});
// Log metrics summary
const totalMetrics = Array.from(this.metrics.values())
.reduce((sum, values) => sum + values.length, 0);
console.log(`Exported ${totalMetrics} metrics across ${this.metrics.size} metric types`);
}
/**
* Clean up old metrics based on retention period
*/
cleanupOldMetrics() {
const cutoffTime = new Date(Date.now() - this.config.retentionPeriod);
for (const [metricName, values] of this.metrics.entries()) {
const filteredValues = values.filter(v => v.timestamp > cutoffTime);
if (filteredValues.length !== values.length) {
this.metrics.set(metricName, filteredValues);
}
}
}
/**
* Get metrics summary
*/
getMetricsSummary() {
let oldestMetric;
let newestMetric;
let totalMetrics = 0;
for (const values of this.metrics.values()) {
totalMetrics += values.length;
values.forEach(value => {
if (!oldestMetric || value.timestamp < oldestMetric) {
oldestMetric = value.timestamp;
}
if (!newestMetric || value.timestamp > newestMetric) {
newestMetric = value.timestamp;
}
});
}
return {
totalMetrics,
metricTypes: this.metrics.size,
oldestMetric,
newestMetric,
isRunning: this.isRunning
};
}
/**
* Clear all metrics
*/
clearMetrics() {
this.metrics.clear();
console.log('All metrics cleared');
this.emit('metricsCleared');
}
}
exports.MetricsService = MetricsService;
//# sourceMappingURL=metrics-service.js.map