136 lines
4.9 KiB
TypeScript
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();
|