Metrics & Monitoring
Open Ethereum Indexer includes built-in support for metrics and monitoring using Prometheus. This page explains how to access, configure, and extend the metrics capabilities of your indexer.
Built-in Metrics
The indexer automatically collects and exposes various metrics about its operation:
- Block processing metrics: Number of blocks processed, processing time
- Event processing metrics: Number of events indexed, processing time
- System metrics: Memory usage, CPU usage
Accessing Metrics
By default, metrics are exposed on the same port as the indexer. They are available at the http://localhost:PORT/metrics
endpoint. However, you most likely don't want these to be publicly accessible.
You can change the metrics port by setting the METRICS_PORT
environment variable or in your application's bootstrap code:
// main.ts
import { NestFactory } from '@nestjs/core';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Create a separate app for metrics
const metricsApp = await NestFactory.create(
PrometheusModule.register({ path: '/metrics' }),
);
await app.listen(3033);
// Configure metrics port
const metricsPort = process.env.METRICS_PORT || 3066;
await metricsApp.listen(metricsPort);
console.log(`Application running on port 3033`);
console.log(`Metrics available at http://localhost:${metricsPort}/metrics`);
}
bootstrap();
You will also need to use the disableMetrics
option in your configuration to disable the metrics on the main app (see below).
Configuring Metrics
Disabling Metrics
If you don't need metrics, you can disable them in your configuration:
const indexerConfig: IndexerConfig = {
// ...other config
app: {
disableMetrics: true,
},
};
Custom Metrics Configuration
You can customize the Prometheus configuration by modifying the module registration:
import { Module } from '@nestjs/common';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
@Module({
imports: [
PrometheusModule.register({
path: '/custom-metrics',
defaultMetrics: {
enabled: true,
config: {},
},
defaultLabels: {
app: 'my-ethereum-indexer',
environment: process.env.NODE_ENV || 'development',
},
}),
],
})
export class AppModule {}
Adding Custom Metrics
You can add your own custom metrics to track specific aspects of your application:
import { Injectable } from '@nestjs/common';
import { InjectMetric } from '@willsoto/nestjs-prometheus';
import { Counter, Gauge, Histogram } from 'prom-client';
@Injectable()
export class CustomMetricsService {
constructor(
@InjectMetric('transfer_count')
private transferCounter: Counter<string>,
@InjectMetric('token_balance')
private tokenBalanceGauge: Gauge<string>,
@InjectMetric('event_processing_time')
private eventProcessingHistogram: Histogram<string>,
) {}
// Track a new transfer
trackTransfer(tokenSymbol: string) {
this.transferCounter.inc({ token: tokenSymbol });
}
// Update token balance
updateTokenBalance(address: string, balance: string) {
this.tokenBalanceGauge.set({ address }, Number(balance));
}
// Track event processing time
trackEventProcessing(eventName: string, timeMs: number) {
this.eventProcessingHistogram.observe({ event: eventName }, timeMs);
}
}
Register the metrics in a module:
import { Module } from '@nestjs/common';
import {
makeCounterProvider,
makeGaugeProvider,
makeHistogramProvider,
} from '@willsoto/nestjs-prometheus';
import { CustomMetricsService } from './custom-metrics.service';
@Module({
providers: [
CustomMetricsService,
makeCounterProvider({
name: 'transfer_count',
help: 'Count of transfers by token',
labelNames: ['token'],
}),
makeGaugeProvider({
name: 'token_balance',
help: 'Token balance by address',
labelNames: ['address'],
}),
makeHistogramProvider({
name: 'event_processing_time',
help: 'Event processing time in milliseconds',
labelNames: ['event'],
buckets: [10, 50, 100, 250, 500, 1000, 2500, 5000],
}),
],
exports: [CustomMetricsService],
})
export class MetricsModule {}
Using Metrics in Handlers
You can use your metrics service in your event and block handlers:
import { Injectable, OnModuleInit } from '@nestjs/common';
import { EventManagerService } from '@open-ethereum/indexer';
import { CustomMetricsService } from './metrics/custom-metrics.service';
@Injectable()
export class EventService implements OnModuleInit {
constructor(
private readonly eventManager: EventManagerService,
private readonly metricsService: CustomMetricsService,
) {}
onModuleInit() {
this.eventManager.onEvent('ERC20:Transfer', {
onIndex: async (payload) => {
const startTime = Date.now();
// Process the event
const { from, to, value } = payload.parsedEvent.args;
// Track the metrics
this.metricsService.trackTransfer('ERC20');
this.metricsService.trackEventProcessing(
'Transfer',
Date.now() - startTime,
);
},
});
}
}
Monitoring with Prometheus and Grafana
These metrics can be scraped by Prometheus and visualized in Grafana dashboards.
Sample Prometheus Configuration
# prometheus.yml
scrape_configs:
- job_name: 'ethereum-indexer'
scrape_interval: 15s
static_configs:
- targets: ['localhost:3066']
Pre-built Dashboards
The indexer comes with two pre-built Grafana dashboards that you can import:
Indexer Dashboard
The Indexer dashboard provides key metrics about your indexer's performance:
- Current block and latest indexed block status
- Blocks behind (lag indicator)
- Block indexing rate
- Reorg detection
- Log distribution per block
- Block processing time distribution
Node.js Dashboard
The Node.js dashboard monitors the health of your indexer process:
- Memory usage and garbage collection
- Event loop lag
- CPU usage
- Active handles and requests
You can find the JSON configurations for these dashboards in the repository and import them directly into your Grafana instance. If you're using the docker image, the dashboards will already be configured.