@houmak/minerva-mcp-server
Version:
Minerva Model Context Protocol (MCP) Server for Microsoft 365 and Azure integrations
199 lines (198 loc) • 6.74 kB
JavaScript
import { metrics, trace } from '@opentelemetry/api';
import { logger } from '../logger.js';
export class AdvancedMonitoring {
config;
meter;
tracer;
// Métriques personnalisées
requestCounter;
responseTimeHistogram;
errorCounter;
circuitBreakerStateGauge;
cacheHitRatioGauge;
activeConnectionsGauge;
constructor(config) {
this.config = config;
}
async initialize() {
if (!this.config.enabled) {
logger.info('Advanced monitoring is disabled');
return;
}
try {
// Initialiser les métriques personnalisées
this.initializeCustomMetrics();
logger.info('Advanced monitoring initialized successfully');
}
catch (error) {
logger.error('Error initializing advanced monitoring:', error);
throw error;
}
}
getHeaders() {
const headers = {};
if (this.config.applicationInsightsConnectionString) {
headers['x-ms-connection-string'] = this.config.applicationInsightsConnectionString;
}
if (this.config.applicationInsightsInstrumentationKey) {
headers['x-ms-instrumentation-key'] = this.config.applicationInsightsInstrumentationKey;
}
return headers;
}
initializeCustomMetrics() {
this.meter = metrics.getMeter(this.config.serviceName);
this.tracer = trace.getTracer(this.config.serviceName);
// Compteur de requêtes
this.requestCounter = this.meter.createCounter('minerva_requests_total', {
description: 'Total number of requests',
});
// Histogramme des temps de réponse
this.responseTimeHistogram = this.meter.createHistogram('minerva_response_time_seconds', {
description: 'Response time in seconds',
unit: 's',
});
// Compteur d'erreurs
this.errorCounter = this.meter.createCounter('minerva_errors_total', {
description: 'Total number of errors',
});
// Gauge pour l'état du circuit breaker
this.circuitBreakerStateGauge = this.meter.createUpDownCounter('minerva_circuit_breaker_state', {
description: 'Circuit breaker state (0=closed, 1=half-open, 2=open)',
});
// Gauge pour le ratio de cache
this.cacheHitRatioGauge = this.meter.createUpDownCounter('minerva_cache_hit_ratio', {
description: 'Cache hit ratio percentage',
});
// Gauge pour les connexions actives
this.activeConnectionsGauge = this.meter.createUpDownCounter('minerva_active_connections', {
description: 'Number of active connections',
});
}
// Méthodes pour les métriques
recordRequest(provider, action, success) {
if (!this.config.enabled)
return;
this.requestCounter.add(1, {
provider,
action,
success: success.toString(),
});
}
recordResponseTime(provider, action, durationMs) {
if (!this.config.enabled)
return;
this.responseTimeHistogram.record(durationMs / 1000, {
provider,
action,
});
}
recordError(provider, action, errorType) {
if (!this.config.enabled)
return;
this.errorCounter.add(1, {
provider,
action,
error_type: errorType,
});
}
updateCircuitBreakerState(provider, state) {
if (!this.config.enabled)
return;
const stateValue = state === 'closed' ? 0 : state === 'half-open' ? 1 : 2;
this.circuitBreakerStateGauge.add(stateValue, {
provider,
});
}
updateCacheHitRatio(provider, hitRatio) {
if (!this.config.enabled)
return;
this.cacheHitRatioGauge.add(hitRatio, {
provider,
});
}
updateActiveConnections(provider, count) {
if (!this.config.enabled)
return;
this.activeConnectionsGauge.add(count, {
provider,
});
}
// Méthodes pour les traces
startSpan(name, attributes) {
if (!this.config.enabled)
return null;
return this.tracer.startSpan(name, {
attributes,
});
}
addSpanEvent(span, name, attributes) {
if (!this.config.enabled || !span)
return;
span.addEvent(name, attributes);
}
setSpanAttributes(span, attributes) {
if (!this.config.enabled || !span)
return;
span.setAttributes(attributes);
}
endSpan(span, status) {
if (!this.config.enabled || !span)
return;
if (status) {
span.setStatus(status);
}
span.end();
}
// Wrapper pour les opérations avec monitoring automatique
async monitorOperation(operation, operationName, provider, attributes) {
if (!this.config.enabled) {
return await operation();
}
const startTime = Date.now();
const span = this.startSpan(operationName, {
provider,
...attributes,
});
try {
const result = await operation();
const duration = Date.now() - startTime;
this.recordRequest(provider, operationName, true);
this.recordResponseTime(provider, operationName, duration);
this.endSpan(span, { code: 1 }); // OK
return result;
}
catch (error) {
const duration = Date.now() - startTime;
const errorType = error instanceof Error ? error.constructor.name : 'Unknown';
this.recordRequest(provider, operationName, false);
this.recordResponseTime(provider, operationName, duration);
this.recordError(provider, operationName, errorType);
this.endSpan(span, { code: 2, message: error instanceof Error ? error.message : 'Unknown error' }); // ERROR
throw error;
}
}
// Métriques de santé
getHealthMetrics() {
if (!this.config.enabled) {
return { enabled: false };
}
return {
enabled: true,
serviceName: this.config.serviceName,
serviceVersion: this.config.serviceVersion,
environment: this.config.environment,
timestamp: new Date().toISOString(),
};
}
// Nettoyage
async shutdown() {
logger.info('Advanced monitoring shutdown completed');
}
// Configuration
getConfig() {
return { ...this.config };
}
isEnabled() {
return this.config.enabled;
}
}