For AI agents: the documentation index is at /llms.txt. Markdown versions of pages are available by appending .md to the URL.
Skip to main content

Observability

HyperIndex gives you several complementary ways to understand what your indexer is doing — while you develop locally and once it's running in production:

Endpoints & port

When the indexer starts it serves an HTTP server (default port 9898) that exposes /metrics, /metrics/runtime, and /healthz. Change the port with ENVIO_INDEXER_PORT:

ENVIO_INDEXER_PORT=9899 envio start

Terminal UI

Running envio dev (or envio start) launches a live Terminal UI (TUI) that shows the state of your indexer at a glance:

  • Per-chain sync status and block numbers
  • Events processed and indexing progress
  • The GraphQL (Hasura) endpoint, and the Dev Console URL when running envio dev
  • HyperSync rate-limit information when you're being throttled

Envio Terminal UI

Disabling the TUI

The TUI is enabled by default in interactive terminals. It is automatically disabled when the process is non-interactive — when stdout is not a TTY, in CI (CI is set), under AI coding agents, or when TERM=dumb.

To disable it explicitly (for example, to capture plain logs), set ENVIO_TUI or pass the CLI flag:

export ENVIO_TUI=false   # or: envio dev --tui-off
Keep the TUI while capturing logs

The TUI can sometimes obscure errors. To keep the dashboard and write full logs to a file at the same time:

export LOG_STRATEGY="both-prettyconsole"
export LOG_FILE="./debug.log"

Logs

HyperIndex uses pino for high-performance structured logging. Logs come from two sources: the indexer runtime, and your own handler code.

Log levels

The runtime supports the following levels, in ascending order of severity. Levels prefixed with u are user-level logs emitted from your handlers and loaders via the context.log API.

LevelDescription
traceMost verbose; detailed tracing information
debugDebugging information for developers
infoGeneral information about system operation (default)
udebugUser-level debug logs
uinfoUser-level info logs
uwarnUser-level warning logs
uerrorUser-level error logs
warnSystem warnings
errorSystem errors
fatalCritical errors causing shutdown

Configure levels with environment variables:

LOG_LEVEL="info"        # Level for console output (default: info)
FILE_LOG_LEVEL="trace" # Level for file output (default: trace)

Use LOG_LEVEL="silent" to suppress console logs entirely.

Log format

The default format is human-readable with color-coded levels. Switch formats with LOG_STRATEGY — the ECS formats are convenient for shipping logs to the Elastic Stack (Kibana):

LOG_STRATEGY="console-pretty"      # Default: human-readable, colored console logs
LOG_STRATEGY="console-raw" # Raw pino JSON to console
LOG_STRATEGY="ecs-file" # ECS-formatted JSON to a file
LOG_STRATEGY="ecs-console" # ECS-formatted JSON to console
LOG_STRATEGY="file-only" # pino JSON to a file (most efficient)
LOG_STRATEGY="both-prettyconsole" # Pretty console + pino JSON to a file

# Where file-based strategies write to (default: logs/envio.log)
LOG_FILE="./logs/envio.log"

User logs

Inside your handlers, use the logging methods on the context object. They map to the u* levels above:

// Inside your handler
context.log.debug(`Processing event in block: ${event.block.number}`);
context.log.info(`Handled transfer of ${event.params.value}`);
context.log.warn(`Unexpected state for ${event.params.from}`);
context.log.error(`Failed to process event: ${event.transaction.hash}`);

// Pass an Error as the second argument to capture a stack trace:
context.log.error("Failed to process event", new Error("boom"));

// Or pass an object for structured logging:
context.log.info("Processing blockchain event", {
blockNumber: event.block.number,
contract: "ERC20",
data: { from: event.params.from, to: event.params.to },
});

For production analytics, pair the ECS log strategies with a tool like Kibana to build dashboards and alerts from your logs.

Performance

Logging isn't free — excessive logging (especially per-event debug/trace logs on high-throughput chains) can slow indexing down. Keep only the logs that serve a real observability need, and lean on metrics rather than logs for high-frequency signals.

Metrics

HyperIndex exposes Prometheus metrics so you can plug into your existing monitoring stack (Prometheus, Grafana, Datadog, etc.).

Stability

As of v3.0.0 the /metrics endpoint is official: metric names follow Prometheus conventions, time is measured in seconds, and the metric surface is covered by semver and documented here.

The /metrics endpoint

The running indexer serves Prometheus metrics at:

http://localhost:9898/metrics

A separate /metrics/runtime endpoint exposes Node.js process metrics (CPU, memory, garbage collection, event-loop lag) from a dedicated registry, isolated from the index metrics:

http://localhost:9898/metrics/runtime

Fetching metrics from the CLI

You don't need to curl the endpoint by hand — the CLI fetches metrics from a locally running indexer for you:

envio metrics          # raw Prometheus metrics from /metrics
envio metrics runtime # runtime metrics from /metrics/runtime

The command resolves the port automatically from ENVIO_INDEXER_PORT in your shell session or the .env file at your project root, falling back to 9898.

Available metrics

Every metric is prefixed with envio_. Counters and gauges are labeled where noted (e.g. by chainId, contract, event).

Progress & sync status

MetricTypeDescription
envio_progress_blockgaugeLatest block number processed and stored in the database. Labeled by chainId.
envio_progress_eventsgaugeNumber of events processed and reflected in the database. Labeled by chainId.
envio_progress_readygaugeWhether the chain is fully synced to the head (1 = synced). Labeled by chainId.

Event processing

MetricTypeDescription
envio_processing_secondscounterCumulative time spent executing event handlers during batch processing.
envio_processing_handler_secondscounterCumulative time spent inside individual event handler executions. Labeled by contract and event.
envio_processing_handler_totalcounterTotal number of individual event handler executions. Labeled by contract and event.
envio_processing_max_batch_sizegaugeMaximum number of items to process in a single batch.

Entity preloading

MetricTypeDescription
envio_preload_secondscounterCumulative time spent preloading entities during batch processing.
envio_preload_handler_secondscounterWall-clock time spent inside individual preload handler executions. Labeled by contract and event.
envio_preload_handler_seconds_totalcounterCumulative time spent in preload handlers (can exceed wall-clock time due to parallel execution). Labeled by contract and event.
envio_preload_handler_totalcounterTotal number of individual preload handler executions. Labeled by contract and event.

Storage

MetricTypeDescription
envio_storage_write_secondscounterCumulative time spent writing batch data to storage.
envio_storage_write_totalcounterTotal number of batch writes to storage.
envio_storage_load_secondscounterTime spent loading data from storage. Labeled by operation.
envio_storage_load_totalcounterNumber of successful storage load operations. Labeled by operation.
envio_storage_load_sizecounterCumulative number of records loaded from storage. Labeled by operation.

Data source & fetching

MetricTypeDescription
envio_fetching_block_range_secondscounterCumulative time spent fetching block ranges. Labeled by chainId.
envio_fetching_block_range_totalcounterTotal number of block range fetch operations. Labeled by chainId.
envio_fetching_block_range_events_totalcounterCumulative number of events fetched across all block range operations. Labeled by chainId.
envio_fetching_block_range_sizecounterCumulative number of blocks covered across all fetch operations. Labeled by chainId.
envio_source_request_totalcounterNumber of requests made to data sources. Labeled by source, chainId, and method.
envio_source_known_heightgaugeLatest known block number reported by the data source. Labeled by source and chainId.

Indexing pipeline

MetricTypeDescription
envio_indexing_known_heightgaugeLatest known block number reported by the active indexing source. Labeled by chainId.
envio_indexing_concurrencygaugeNumber of executing concurrent queries to the chain data source. Labeled by chainId.
envio_indexing_buffer_sizegaugeCurrent number of items in the indexing buffer. Labeled by chainId.
envio_indexing_buffer_blockgaugeHighest block number fully fetched by the indexer. Labeled by chainId.
envio_indexing_idle_secondscounterTime the indexer source syncing has been idle. A high value may indicate a bottleneck. Labeled by chainId.
envio_indexing_partitionsgaugeNumber of partitions used to split fetching logic. Labeled by chainId.
envio_indexing_addressesgaugeNumber of addresses indexed on chain (static and dynamic). Labeled by chainId.

Reorgs & rollbacks

MetricTypeDescription
envio_reorg_detected_totalcounterTotal number of reorgs detected.
envio_reorg_detected_blockgaugeBlock number where a reorg was last detected.
envio_reorg_thresholdgaugeWhether indexing is currently within the reorg threshold.
envio_rollback_enabledgaugeWhether rollback on reorg is enabled.
envio_rollback_totalcounterNumber of successful rollbacks on reorg.
envio_rollback_secondscounterTotal time spent on rollbacks.
envio_rollback_eventscounterNumber of events rolled back on reorg.

Effect API

Metrics for the Effect API.

MetricTypeDescription
envio_effect_call_secondscounterProcessing time taken to call the Effect function. Labeled by effect.
envio_effect_call_totalcounterCumulative number of resolved Effect function calls. Labeled by effect.
envio_effect_active_callsgaugeNumber of Effect function calls currently running. Labeled by effect.
envio_effect_cachegaugeNumber of items in the effect cache. Labeled by effect.
envio_effect_queuegaugeNumber of effect calls waiting in the rate-limit queue.

System info

MetricTypeDescription
envio_infogaugeInformation about the indexer. Labeled by version.
envio_process_start_time_secondsgaugeStart time of the process since the Unix epoch, in seconds.

Scraping with Prometheus

Add a scrape job to your prometheus.yml:

scrape_configs:
- job_name: "envio-indexer"
metrics_path: "/metrics"
static_configs:
- targets: ["localhost:9898"]

Key metrics to watch

  • envio_progress_ready — confirm your indexer is caught up to the chain head.
  • envio_progress_block — track indexing progress over time.
  • envio_processing_handler_seconds — identify slow event handlers by contract and event.
  • envio_indexing_idle_seconds — a high value may indicate the data source sync is a bottleneck.
  • envio_reorg_detected_total — track how frequently chain reorganizations occur.

Health checks

The indexer exposes a /healthz endpoint that returns HTTP 200 once the service is up. It's designed as a machine-readable liveness probe — for example, a Kubernetes livenessProbe:

GET http://localhost:9898/healthz  →  200 OK
livenessProbe:
httpGet:
path: /healthz
port: 9898
initialDelaySeconds: 10
periodSeconds: 10

Indexing status

To track per-chain indexing progress from your application, query the official _meta field on the GraphQL API. This is the most reliable way to know whether a chain has caught up before you read indexed data — useful for health checks, custom dashboards, and waiting for a write to be indexed before refetching.

{
_meta {
chainId
progressBlock
eventsProcessed
bufferBlock
firstEventBlock
sourceBlock
readyAt
isReady
startBlock
endBlock
}
}

Result:

{
"data": {
"_meta": [
{
"chainId": 1,
"progressBlock": 22817138,
"eventsProcessed": 2380000,
"bufferBlock": 22820499,
"firstEventBlock": 21688545,
"sourceBlock": 23368264,
"readyAt": null,
"isReady": false,
"startBlock": 0,
"endBlock": null
},
{
"chainId": 10,
"progressBlock": 137848820,
"eventsProcessed": 2455000,
"bufferBlock": 137873621,
"firstEventBlock": 130990676,
"sourceBlock": 141168975,
"readyAt": null,
"isReady": false,
"startBlock": 0,
"endBlock": null
}
]
}
}

Metadata fields

Configuration fields

These fields are populated on indexer startup and don't change during the indexer process.

  • chainId — Chain ID the metadata belongs to. Results are sorted by chainId in ascending order. Use _meta(where: { chainId: { _eq: 1 } }) to get the metadata for a specific chain.
  • startBlock — Start block number from config.yaml.
  • endBlock — End block number from config.yaml.

Transactional fields

These fields are updated in the batch write transaction, and are guaranteed to be written to the database at the same time. This means progressBlock and eventsProcessed increase at the same moment the data for the processed events is written to the database and becomes available for querying.

  • progressBlock — Block number fully processed and written to the DB.
  • eventsProcessed — Number of processed events written to the DB (reorg resistant).

Throttled fields

These fields are updated outside of the batch transaction and throttled to avoid performance overhead. There might be a small delay between the event processing and the metadata update.

  • bufferBlock — Block number of the latest event ready for processing.
  • firstEventBlock — Block number of the first processed event for the chain.
  • sourceBlock — The latest known block number of the actively used data source (the chain head).
  • readyAt — Timestamp when the chain finished historical sync or reached its end block.
  • isReady — Whether the chain finished historical sync or reached its end block.

Dev Console

When you run envio dev, HyperIndex enables the Dev Console — a web UI for debugging your indexer during local development, available at envio.dev/console. The TUI also prints the link on startup.

The Dev Console connects to your locally running indexer, so your data never leaves your machine. It's available in development mode only — envio start (production) does not expose console state.

Use it to inspect indexing state and to manage the Effect API cache while iterating.

Local GraphQL (Hasura)

envio dev also starts a local Hasura console at http://localhost:8080 for exploring your indexed data. The default admin secret is testing. Disable Hasura with ENVIO_HASURA=false.

Envio Cloud

If you deploy to Envio Cloud, observability is fully managed for you — no need to run Prometheus or ship logs yourself:

  • Real-time dashboard — sync status, events processed, and per-network progress bars.
  • Logs — live and historical logs with level filtering, integrated and configured by Envio.
  • Built-in alerts — get notified via Discord, Slack, Telegram, Email, or a generic webhook when your endpoint goes down or your indexer stops processing.

Envio Cloud also exposes the same Prometheus metrics described above, so you can scrape them into your own Grafana or Datadog. On Cloud the endpoint is served under your deployment's URL:

<your-endpoint-url>/hyperindex/metrics
Version requirement

Prometheus metrics on Envio Cloud require indexers deployed with version 3.0.0 or higher.

See Monitoring Your Indexer and the Envio Cloud features page for details, and Envio Cloud CLI for monitoring deployments from the command line.