feat(eth2api): add Charon-parity beacon node request metrics - #573
Merged
Conversation
Adds `app_eth2_requests_total`, `app_eth2_errors_total`, and `app_eth2_latency_seconds` (with Charon's exact latency buckets), labelled by logical endpoint, mirroring Charon's eth2wrap metrics. Defined via the workspace `vise` conventions and auto-registered, so the existing `/metrics` endpoint surfaces them with no extra wiring. A small `instrument()` helper reproduces Charon's per-call semantics (record request + latency always, error on `Err`) and slots in ahead of the existing response handling. Instruments the endpoints Pluto calls today using Charon's snake_case labels: inside existing eth2api wrappers (attester duties, all submit duties, spec, genesis, fork_schedule, validators) and inline at the core/app call sites (proposal, attestation_data, sync_committee_contribution, aggregate_attestation, proposer_duties, sync_committee_duties, node_syncing, node_version, node_peer_count). The `using_fallback` gauge is omitted until a multi/fallback beacon client exists (#402 part B).
…-metrics # Conflicts: # crates/core/src/scheduler.rs # crates/eth2api/src/extensions.rs
Locks in the `/metrics` OpenMetrics-for-Prometheus series names, the `endpoint` label, and the monitoring API's global `cluster_*` labels that the beacon-node health Grafana panel queries, guarding against a double `_total` counter suffix.
iamquang95
approved these changes
Jul 30, 2026
…l sites Addresses PR #573 review: - errors_total now counts non-2xx beacon-node responses, not just transport/decode Err. The generated client returns Ok(Response::BadRequest(..)) for HTTP errors, so is_err() alone never fired. A new BeaconResponse trait (impl'd per response enum via macro) lets instrument classify 2xx vs error, matching Charon eth2wrap semantics. - Wrap the two remaining get_attester_duties call sites (scheduler fetch_attester_duties and the validatorapi handler) in instrument("attester_duties", ..). Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Charon
eth2wrap-parity Prometheus metrics for beacon node requests, using Pluto'sviseconventions (not theprometheuscrate, which Pluto doesn't use):app_eth2_requests_total{endpoint}app_eth2_errors_total{endpoint}app_eth2_latency_seconds{endpoint}— with Charon's exact 17 latency bucketsMetric names match Charon's
app_eth2_*series. They're defined in a newcrates/eth2api/src/metrics.rsand auto-registered via#[vise::register], so the existing/metricsendpoint surfaces them with no extra wiring.A small
instrument(endpoint, fut)helper reproduces Charon's per-call semantics — record a request + observe latency always, increment errors onErr— and slots in ahead of the existing.map_err(...)handling at each call site (the generated client methods returnanyhow::Result, so caller types are preserved).Coverage
Instruments the endpoints Pluto actually calls today, using Charon's snake_case labels:
eth2apiwrappers (zero call-site churn):attester_duties, allsubmit_*duties,spec,genesis,fork_schedule,validators.core/appcall sites:proposal,attestation_data,sync_committee_contribution,aggregate_attestation,proposer_duties,sync_committee_duties,node_syncing,node_version,node_peer_count.Endpoints Charon instruments but Pluto doesn't call yet (e.g.
beacon_block_root, the*_selections,prepare_sync_committee_subnets) are intentionally skipped — each is now a one-lineinstrument(...)addition when the port reaches them.Deliberate deviations from Charon
using_fallbackgauge omitted — Pluto has no multi/fallback beacon client yet (Implementapp/app.goequivalent — node graph wiring & lifecycle #402 part B). To be added when fallback lands.errors_totalcounts transport/decodeErronly, not HTTP 4xx/5xx. Charon's go-eth2-client returns an error on non-2xx; Pluto's generated client returnsOk(Response::BadRequest(..))for 4xx, which wrappers convert toErrafter the instrumented call. Full error-count parity would require instrumenting around response classification — can follow up if exact parity is wanted.Testing
cargo +nightly fmt --all --check✅cargo clippy -p pluto-eth2api -p pluto-core -p pluto-app --all-targets --all-features✅ (clean)cargo test: eth2api (119) ✅, core (600) ✅, app ✅ — includes new unit tests asserting the exposition text and all latency buckets.🤖 Generated with Claude Code