import { InfluxDB, Point, WriteApi } from '@influxdata/influxdb-client'; import { IMetricsProvider } from '../domain/interfaces.js'; import { logger } from '../logging/index.js'; export class InfluxMetricsProvider implements IMetricsProvider { private writeApi: WriteApi | null = null; private influx: InfluxDB | null = null; constructor(url: string, token: string, org: string, bucket: string) { try { this.influx = new InfluxDB({ url, token }); this.writeApi = this.influx.getWriteApi(org, bucket, 'ms', { writeFailed(error) { logger.error('InfluxDB metrics point write failed', error); }, }); logger.info('InfluxDB metrics provider initialized successfully'); } catch (error) { logger.error( 'Failed to initialize InfluxDB metrics provider. System will run with degraded/no time-series metrics reporting.', error, ); } } private writePoint(point: Point): void { if (!this.writeApi) { logger.debug('InfluxDB write client not initialized, discarding point', { measurement: point.toLineProtocol(), }); return; } try { this.writeApi.writePoint(point); } catch (error) { logger.error('Error writing point to InfluxDB', error); } } recordWalletDetectionLatency(latencyMs: number, wallet: string): void { const p = new Point('wallet_detection_latency') .tag('wallet', wallet) .floatField('latency_ms', latencyMs); this.writePoint(p); } recordSignalProcessingLatency(latencyMs: number): void { const p = new Point('signal_processing_latency').floatField('latency_ms', latencyMs); this.writePoint(p); } recordQuoteLatency(latencyMs: number, provider: string): void { const p = new Point('quote_latency').tag('provider', provider).floatField('latency_ms', latencyMs); this.writePoint(p); } recordExecutionLatency(latencyMs: number, scenario: number): void { const p = new Point('execution_latency') .tag('scenario', `${scenario}ms`) .floatField('latency_ms', latencyMs); this.writePoint(p); } recordPrice(tokenMint: string, priceSol: number): void { const p = new Point('token_price').tag('token_mint', tokenMint).floatField('price_sol', priceSol); this.writePoint(p); } recordPortfolioBalance(balanceSol: number, scenario: number): void { const p = new Point('portfolio_balance') .tag('scenario', `${scenario}ms`) .floatField('balance_sol', balanceSol); this.writePoint(p); } recordTradePnl(pnlSol: number, scenario: number, tokenMint: string): void { const p = new Point('trade_pnl') .tag('scenario', `${scenario}ms`) .tag('token_mint', tokenMint) .floatField('pnl_sol', pnlSol); this.writePoint(p); } recordExcursion(mfeSol: number, maeSol: number, scenario: number, tokenMint: string): void { const p = new Point('trade_excursion') .tag('scenario', `${scenario}ms`) .tag('token_mint', tokenMint) .floatField('mfe_sol', mfeSol) .floatField('mae_sol', maeSol); this.writePoint(p); } recordQueueDepth(depth: number): void { const p = new Point('queue_depth').intField('depth', depth); this.writePoint(p); } recordQueueRetries(retries: number, signature: string): void { const p = new Point('queue_retries').tag('signature', signature).intField('retries', retries); this.writePoint(p); } recordQueue429s(signature: string): void { const p = new Point('queue_429s').tag('signature', signature).intField('count', 1); this.writePoint(p); } recordWalletEventReceived(wallet: string): void { const p = new Point('wallet_event_received').tag('wallet', wallet).intField('count', 1); this.writePoint(p); } recordWalletQueueDepth(wallet: string, depth: number): void { const p = new Point('wallet_queue_depth').tag('wallet', wallet).intField('depth', depth); this.writePoint(p); } recordOldestQueuedAge(wallet: string, ageMs: number): void { const p = new Point('oldest_queued_age').tag('wallet', wallet).floatField('age_ms', ageMs); this.writePoint(p); } recordDroppedOldestCount(wallet: string): void { const p = new Point('dropped_oldest_count').tag('wallet', wallet).intField('count', 1); this.writePoint(p); } recordTransactionRelevance(relevant: boolean): void { const p = new Point('transaction_relevance').tag('relevant', String(relevant)).intField('count', 1); this.writePoint(p); } recordRpcRequestRate(requestsPerSecond: number): void { const p = new Point('rpc_request_rate').floatField('req_per_sec', requestsPerSecond); this.writePoint(p); } recordQueueProcessed(wallet: string): void { const p = new Point('queue_processed').tag('wallet', wallet).intField('count', 1); this.writePoint(p); } recordRpcFetchError(wallet: string): void { const p = new Point('rpc_fetch_error').tag('wallet', wallet).intField('count', 1); this.writePoint(p); } recordWalletState(wallet: string, isHot: boolean): void { const p = new Point('wallet_state').tag('wallet', wallet).intField('is_hot', isHot ? 1 : 0); this.writePoint(p); } recordWalletIncomingRate(wallet: string, rate: number): void { const p = new Point('wallet_incoming_rate').tag('wallet', wallet).floatField('rate', rate); this.writePoint(p); } recordWalletProcessedRate(wallet: string, rate: number): void { const p = new Point('wallet_processed_rate').tag('wallet', wallet).floatField('rate', rate); this.writePoint(p); } async close(): Promise { if (this.writeApi) { logger.info('Closing InfluxDB metrics writer...'); try { await this.writeApi.close(); logger.info('InfluxDB metrics writer closed.'); } catch (error) { logger.error('Error closing InfluxDB metrics writer', error); } } } } // In-Memory/Mock Metrics Provider for local tests and offline dev export class MockMetricsProvider implements IMetricsProvider { recordWalletDetectionLatency(_latencyMs: number, _wallet: string): void {} recordSignalProcessingLatency(_latencyMs: number): void {} recordQuoteLatency(_latencyMs: number, _provider: string): void {} recordExecutionLatency(_latencyMs: number, _scenario: number): void {} recordPrice(_tokenMint: string, _priceSol: number): void {} recordPortfolioBalance(_balanceSol: number, _scenario: number): void {} recordTradePnl(_pnlSol: number, _scenario: number, _tokenMint: string): void {} recordExcursion(_mfeSol: number, _maeSol: number, _scenario: number, _tokenMint: string): void {} recordQueueDepth(_depth: number): void {} recordQueueRetries(_retries: number, _signature: string): void {} recordQueue429s(_signature: string): void {} recordWalletEventReceived(_wallet: string): void {} recordWalletQueueDepth(_wallet: string, _depth: number): void {} recordOldestQueuedAge(_wallet: string, _ageMs: number): void {} recordDroppedOldestCount(_wallet: string): void {} recordTransactionRelevance(_relevant: boolean): void {} recordRpcRequestRate(_requestsPerSecond: number): void {} recordQueueProcessed(_wallet: string): void {} recordRpcFetchError(_wallet: string): void {} recordWalletState(_wallet: string, _isHot: boolean): void {} recordWalletIncomingRate(_wallet: string, _rate: number): void {} recordWalletProcessedRate(_wallet: string, _rate: number): void {} }