learning_ai_invt_trdg/backend/src/services/MetricsService.ts

136 lines
4.9 KiB
TypeScript

import { Registry, Counter, Gauge, Histogram, collectDefaultMetrics } from 'prom-client';
import { config } from '../config/index.js';
export class MetricsService {
private static instance: MetricsService;
private registry: Registry;
// --- Metrics Definition ---
// Operational Events
public operationalEventsTotal: Counter<string>;
// Subsystem Metrics
public subsystemDurationSeconds: Histogram<string>;
public subsystemLastRunTimestamp: Gauge<string>;
public subsystemAlive: Gauge<string>;
// Exchange API Metrics
public exchangeApiLatencySeconds: Histogram<string>;
// Risk & Capital Metrics
public capitalInvariantViolationsTotal: Counter<string>;
public profileUtilizationPercent: Gauge<string>;
// Reconciliation Metrics
public reconciliationMismatchesTotal: Counter<string>;
public reconciliationMissingItemsCount: Gauge<string>;
private constructor() {
this.registry = new Registry();
// Dynamic labels for every metric
const env = process.env.NODE_ENV || 'development';
const mode = config.PAPER_TRADING ? 'paper' : 'live';
this.registry.setDefaultLabels({
app: 'bytelyst-trading-bot-service',
env,
mode
});
collectDefaultMetrics({ register: this.registry });
// operational_events_total
this.operationalEventsTotal = new Counter({
name: 'bytelyst_bot_operational_events_total',
help: 'Cumulative count of operational events emitted by the system',
labelNames: ['severity', 'type', 'profile_id', 'symbol'],
registers: [this.registry]
});
// subsystem_duration_seconds
this.subsystemDurationSeconds = new Histogram({
name: 'bytelyst_bot_subsystem_duration_seconds',
help: 'Duration of subsystem loop executions',
labelNames: ['subsystem'],
buckets: [0.1, 0.25, 0.5, 1, 2, 5, 10],
registers: [this.registry]
});
// subsystem_last_run_timestamp
this.subsystemLastRunTimestamp = new Gauge({
name: 'bytelyst_bot_subsystem_last_run_timestamp',
help: 'Unix timestamp of the last successful run of a subsystem',
labelNames: ['subsystem'],
registers: [this.registry]
});
// subsystem_alive
this.subsystemAlive = new Gauge({
name: 'bytelyst_bot_subsystem_alive',
help: 'High-level availability flag for subsystems (1=alive, 0=stalled)',
labelNames: ['subsystem'],
registers: [this.registry]
});
// exchange_api_latency_seconds
this.exchangeApiLatencySeconds = new Histogram({
name: 'bytelyst_bot_exchange_api_latency_seconds',
help: 'Latency of outgoing exchange API requests',
labelNames: ['exchange', 'operation'],
buckets: [0.05, 0.1, 0.25, 0.5, 1, 2, 5],
registers: [this.registry]
});
// capital_invariant_violations_total
this.capitalInvariantViolationsTotal = new Counter({
name: 'bytelyst_bot_capital_invariant_violations_total',
help: 'Cumulative count of capital invariant violations per profile',
labelNames: ['profile_id'],
registers: [this.registry]
});
// profile_utilization_percent
this.profileUtilizationPercent = new Gauge({
name: 'bytelyst_bot_profile_utilization_percent',
help: 'Percentage of allocated capital currently tied up in positions/orders',
labelNames: ['profile_id'],
registers: [this.registry]
});
// reconciliation_mismatches_total
this.reconciliationMismatchesTotal = new Counter({
name: 'bytelyst_bot_reconciliation_mismatches_total',
help: 'Cumulative count of detected state mismatches between DB and Exchange',
registers: [this.registry]
});
// reconciliation_missing_items_count
this.reconciliationMissingItemsCount = new Gauge({
name: 'bytelyst_bot_reconciliation_missing_items_count',
help: 'Count of missing items detected in the last reconciliation cycle',
labelNames: ['source'],
registers: [this.registry]
});
}
public static getInstance(): MetricsService {
if (!MetricsService.instance) {
MetricsService.instance = new MetricsService();
}
return MetricsService.instance;
}
public async getMetrics(): Promise<string> {
return await this.registry.metrics();
}
public getContentType(): string {
return this.registry.contentType;
}
}
export const metrics = MetricsService.getInstance();