Prometheus/OpenTelemetry metrics exporter for the stellar-router suite.
Soroban smart contracts run inside the Stellar network as WASM and cannot open sockets or push metrics themselves. This binary bridges the gap by:
- Polling the Soroban RPC endpoint at a configurable interval
- Reading on-chain state from each router contract (total_routed, total_calls, circuit-breaker state, paused flags, etc.)
- Exposing a
/metricsHTTP endpoint in the Prometheus text format
| Metric | Type | Labels | Description |
|---|---|---|---|
router_core_total_routed |
Gauge | contract |
Cumulative successful route resolutions |
router_core_paused |
Gauge | contract |
1 if the router is globally paused |
router_core_route_paused |
Gauge | contract, route |
1 if a specific route is paused |
router_middleware_total_calls |
Gauge | contract |
Cumulative pre-call invocations |
router_middleware_circuit_open |
Gauge | contract, route |
1 if the circuit breaker is open |
router_middleware_failure_count |
Gauge | contract, route |
Consecutive failure count |
router_registry_total_names |
Gauge | contract |
Total contract names registered |
router_scrape_duration_seconds |
Histogram | contract |
Time spent scraping each contract |
router_scrape_errors_total |
Counter | contract |
Number of failed scrape attempts |
router_up |
Gauge | — | 1 if the last scrape cycle succeeded |
cd metrics
cargo build --releaseThe binary will be at target/release/router-metrics-exporter.
FROM rust:1.83-slim as builder
WORKDIR /build
COPY . .
RUN cargo build --release -p router-metrics-exporter
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/target/release/router-metrics-exporter /usr/local/bin/
ENTRYPOINT ["router-metrics-exporter"]All flags can also be set via environment variables (shown in brackets).
router-metrics-exporter [OPTIONS]
Options:
--rpc-url <URL>
Soroban RPC endpoint URL
[env: ROUTER_RPC_URL]
[default: https://soroban-testnet.stellar.org]
--network-passphrase <PASSPHRASE>
Stellar network passphrase (used to decode XDR correctly)
[env: ROUTER_NETWORK_PASSPHRASE]
[default: Test SDF Network ; September 2015]
--core-contract-id <CONTRACT_ID>
Contract ID of the deployed router-core contract
[env: ROUTER_CORE_CONTRACT_ID]
[default: ]
--middleware-contract-id <CONTRACT_ID>
Contract ID of the deployed router-middleware contract
[env: ROUTER_MIDDLEWARE_CONTRACT_ID]
[default: ]
--registry-contract-id <CONTRACT_ID>
Contract ID of the deployed router-registry contract
[env: ROUTER_REGISTRY_CONTRACT_ID]
[default: ]
--scrape-interval-secs <SECONDS>
How often (in seconds) to poll the Soroban RPC for fresh data
[env: ROUTER_SCRAPE_INTERVAL_SECS]
[default: 15]
--listen <ADDRESS>
Address and port to listen on for the /metrics HTTP endpoint
[env: ROUTER_LISTEN]
[default: 0.0.0.0:9090]
--rpc-timeout-secs <SECONDS>
RPC request timeout in seconds
[env: ROUTER_RPC_TIMEOUT_SECS]
[default: 10]
-h, --help
Print help
-V, --version
Print version
export ROUTER_RPC_URL="https://soroban-testnet.stellar.org"
export ROUTER_CORE_CONTRACT_ID="CBGTG...YOUR_CONTRACT_ID"
export ROUTER_MIDDLEWARE_CONTRACT_ID="CBGTG...YOUR_CONTRACT_ID"
export ROUTER_REGISTRY_CONTRACT_ID="CBGTG...YOUR_CONTRACT_ID"
export ROUTER_SCRAPE_INTERVAL_SECS=30
export ROUTER_LISTEN="0.0.0.0:9090"
./target/release/router-metrics-exporterexport ROUTER_RPC_URL="https://soroban-mainnet.stellar.org"
export ROUTER_NETWORK_PASSPHRASE="Public Global Stellar Network ; September 2015"
export ROUTER_CORE_CONTRACT_ID="CBGTG...YOUR_CONTRACT_ID"
export ROUTER_MIDDLEWARE_CONTRACT_ID="CBGTG...YOUR_CONTRACT_ID"
export ROUTER_SCRAPE_INTERVAL_SECS=60
./target/release/router-metrics-exporterAdd the exporter as a scrape target in your prometheus.yml:
scrape_configs:
- job_name: 'stellar-router'
scrape_interval: 30s
static_configs:
- targets: ['localhost:9090']
labels:
environment: 'testnet'
service: 'stellar-router'Example queries for a Grafana dashboard:
rate(router_core_total_routed{contract="CBGTG..."}[5m]) * 60
router_middleware_circuit_open{contract="CBGTG...", route="oracle/get_price"}
router_up
histogram_quantile(0.95, rate(router_scrape_duration_seconds_bucket[5m]))
rate(router_scrape_errors_total[5m])
The exporter calls view functions on each contract via simulateTransaction:
- router-core:
total_routed(),get_all_routes(),get_route(name)for each route - router-middleware:
total_calls(),get_configured_routes(),circuit_breaker_state(route)for each route - router-registry:
get_all_names()(total count)
Each contract scrape is timed and any error increments the router_scrape_errors_total counter.
- Scrape interval: Default 15s. Increase for mainnet (30-60s) to reduce RPC load.
- RPC timeout: Default 10s. Increase if you see frequent timeout errors.
- Overhead: Minimal — the exporter makes 1-3 RPC calls per contract per scrape cycle.
- No transaction-level latency: The exporter tracks scrape latency (off-chain polling time), not on-chain transaction latency. For transaction-level metrics, use Stellar Horizon's transaction history API.
- No real-time events: Metrics are updated on the scrape interval (default 15s), not in real-time. For real-time monitoring, consider streaming Stellar ledger events via Horizon.
- XDR encoding: The current implementation uses JSON-RPC simulation results. For production deployments with complex data types, integrate the
stellar-xdrcrate for proper XDR encoding/decoding.
The exporter exposes Prometheus metrics. To send metrics to an OpenTelemetry collector:
- Use the Prometheus receiver in your OTel collector config:
receivers:
prometheus:
config:
scrape_configs:
- job_name: 'stellar-router'
scrape_interval: 30s
static_configs:
- targets: ['router-metrics-exporter:9090']
exporters:
otlp:
endpoint: "otel-collector:4317"
service:
pipelines:
metrics:
receivers: [prometheus]
exporters: [otlp]- Or use the Prometheus remote write exporter to send to an OTel-compatible backend (e.g., Grafana Cloud, Datadog, New Relic).
The contract may not expose the view function being called. Verify the contract is deployed and initialized:
stellar contract invoke --id <CONTRACT_ID> --network testnet -- total_routedThe RPC endpoint may be down or rate-limiting your requests. Check:
- RPC endpoint is reachable:
curl https://soroban-testnet.stellar.org - Increase
--rpc-timeout-secsif requests are timing out - Increase
--scrape-interval-secsto reduce request rate
At least one contract scrape failed. Check logs for details:
RUST_LOG=router_metrics_exporter=debug ./router-metrics-exporter- Reduce the number of routes/configured routes being scraped
- Increase
--scrape-interval-secsto reduce load - Use a dedicated RPC endpoint (not the public one) for production
cargo test -p router-metrics-exportercargo run -p router-metrics-exporter -- \
--core-contract-id "CBGTG..." \
--scrape-interval-secs 10RUST_LOG=router_metrics_exporter=debug cargo run -p router-metrics-exporterMIT (same as the parent stellar-router project)