Metrics & Monitoring

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

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

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.