From 53a26988171bf88fa4841dc0e821f8cc55d02f32 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Thu, 14 Sep 2023 14:18:26 +0200 Subject: [PATCH] feat: expose rank & active set metrics --- README.md | 4 ++- pkg/exporter/exporter.go | 6 +++++ pkg/exporter/exporter_test.go | 46 ++++++++++++++++++++++++----------- pkg/metrics/metrics.go | 19 +++++++++++++++ 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1276668..2793b6e 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,10 @@ GLOBAL OPTIONS: All metrics are by default prefixed by `cosmos_validator_watcher` but this can be changed through options. Metrics (without prefix) | Description -----------------------------------------------|------------------------------------------------ +-------------------------|------------------------------------------------ `block_height` | Latest known block height (all nodes mixed up) +`rank` | Rank of the validator (or 0 is not bonded) +`active_set` | Number of validators in the active set `validated_blocks` | Number of validated blocks per validator (for a bonded validator) `missed_blocks` | Number of missed blocks per validator (for a bonded validator) `tracked_blocks` | Number of blocks tracked since start diff --git a/pkg/exporter/exporter.go b/pkg/exporter/exporter.go index 68a83ae..7d04d2a 100644 --- a/pkg/exporter/exporter.go +++ b/pkg/exporter/exporter.go @@ -75,6 +75,7 @@ type ValidatorStatus struct { Label string Bonded bool Signed bool + Rank int } func (e *Exporter) handleBlock(block *types.Block) { @@ -91,6 +92,7 @@ func (e *Exporter) handleBlock(block *types.Block) { e.latestBlockHeight = block.Header.Height e.cfg.Metrics.BlockHeight.Set(float64(block.Header.Height)) + e.cfg.Metrics.ActiveSet.Set(float64(len(block.LastCommit.Signatures))) e.cfg.Metrics.TrackedBlocks.Inc() result := BlockResult{ @@ -109,6 +111,7 @@ func (e *Exporter) handleBlock(block *types.Block) { for _, val := range e.cfg.TrackedValidators { bonded := false signed := false + rank := 0 for i, sig := range block.LastCommit.Signatures { if sig.ValidatorAddress.String() == "" { log.Warn().Msgf("empty validator address at pos %d", i) @@ -116,6 +119,8 @@ func (e *Exporter) handleBlock(block *types.Block) { if val.Address == sig.ValidatorAddress.String() { bonded = true signed = !sig.Absent() + rank = i + 1 + e.cfg.Metrics.Rank.WithLabelValues(val.Address, val.Name).Set(float64(rank)) } if signed { break @@ -126,6 +131,7 @@ func (e *Exporter) handleBlock(block *types.Block) { Label: val.Name, Bonded: bonded, Signed: signed, + Rank: rank, }) } diff --git a/pkg/exporter/exporter_test.go b/pkg/exporter/exporter_test.go index d168a77..95496eb 100644 --- a/pkg/exporter/exporter_test.go +++ b/pkg/exporter/exporter_test.go @@ -19,8 +19,10 @@ import ( func TestExporter(t *testing.T) { var ( - address = "3DC4DD610817606AD4A8F9D762A068A81E8741E2" - name = "Kiln" + kilnAddress = "3DC4DD610817606AD4A8F9D762A068A81E8741E2" + kilnName = "Kiln" + + miscAddress = "1234567890ABCDEF10817606AD4A8FD7620A81E4" ) exporter := New(&Config{ @@ -30,8 +32,8 @@ func TestExporter(t *testing.T) { ValidatorsChan: make(chan []stakingtypes.Validator), TrackedValidators: []TrackedValidator{ { - Address: address, - Name: name, + Address: kilnAddress, + Name: kilnName, }, }, }) @@ -68,7 +70,7 @@ func TestExporter(t *testing.T) { Signatures: []types.CommitSig{ { BlockIDFlag: types.BlockIDFlagAbsent, - ValidatorAddress: MustParseAddress(address), + ValidatorAddress: MustParseAddress(kilnAddress), }, }, }, @@ -77,9 +79,13 @@ func TestExporter(t *testing.T) { Header: types.Header{Height: 41}, LastCommit: &types.Commit{ Signatures: []types.CommitSig{ + { + BlockIDFlag: types.BlockIDFlagAbsent, + ValidatorAddress: MustParseAddress(miscAddress), + }, { BlockIDFlag: types.BlockIDFlagCommit, - ValidatorAddress: MustParseAddress(address), + ValidatorAddress: MustParseAddress(kilnAddress), }, }, }, @@ -90,7 +96,11 @@ func TestExporter(t *testing.T) { Signatures: []types.CommitSig{ { BlockIDFlag: types.BlockIDFlagCommit, - ValidatorAddress: MustParseAddress(address), + ValidatorAddress: MustParseAddress(miscAddress), + }, + { + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: MustParseAddress(kilnAddress), }, }, }, @@ -105,8 +115,8 @@ func TestExporter(t *testing.T) { strings.Join([]string{ `#35 0/1 validators ⚪️ Kiln`, `#40 0/1 validators ❌ Kiln`, - `#41 1/1 validators ✅ Kiln`, - `#42 1/1 validators ✅ Kiln`, + `#41 1/2 validators ✅ Kiln`, + `#42 2/2 validators ✅ Kiln`, }, "\n")+"\n", exporter.cfg.Writer.(*bytes.Buffer).String(), ) @@ -115,6 +125,10 @@ func TestExporter(t *testing.T) { `gauge: `, ReadMetric(exporter.cfg.Metrics.BlockHeight), ) + assert.Equal(t, + `gauge: `, + ReadMetric(exporter.cfg.Metrics.ActiveSet), + ) assert.Equal(t, `counter: `, ReadMetric(exporter.cfg.Metrics.TrackedBlocks), @@ -123,13 +137,17 @@ func TestExporter(t *testing.T) { `counter: `, ReadMetric(exporter.cfg.Metrics.SkippedBlocks), ) + assert.Equal(t, + `label: label: gauge: `, + ReadMetric(exporter.cfg.Metrics.Rank.WithLabelValues(kilnAddress, kilnName)), + ) assert.Equal(t, `label: label: counter: `, - ReadMetric(exporter.cfg.Metrics.ValidatedBlocks.WithLabelValues(address, name)), + ReadMetric(exporter.cfg.Metrics.ValidatedBlocks.WithLabelValues(kilnAddress, kilnName)), ) assert.Equal(t, `label: label: counter: `, - ReadMetric(exporter.cfg.Metrics.MissedBlocks.WithLabelValues(address, name)), + ReadMetric(exporter.cfg.Metrics.MissedBlocks.WithLabelValues(kilnAddress, kilnName)), ) }) @@ -158,15 +176,15 @@ func TestExporter(t *testing.T) { assert.Equal(t, `label: label: gauge: `, - ReadMetric(exporter.cfg.Metrics.BondedTokens.WithLabelValues(address, name)), + ReadMetric(exporter.cfg.Metrics.BondedTokens.WithLabelValues(kilnAddress, kilnName)), ) assert.Equal(t, `label: label: gauge: `, - ReadMetric(exporter.cfg.Metrics.IsBonded.WithLabelValues(address, name)), + ReadMetric(exporter.cfg.Metrics.IsBonded.WithLabelValues(kilnAddress, kilnName)), ) assert.Equal(t, `label: label: gauge: `, - ReadMetric(exporter.cfg.Metrics.IsJailed.WithLabelValues(address, name)), + ReadMetric(exporter.cfg.Metrics.IsJailed.WithLabelValues(kilnAddress, kilnName)), ) }) } diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index edbf579..ddd395a 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -5,6 +5,8 @@ import "github.com/prometheus/client_golang/prometheus" type Metrics struct { // Exporter metrics BlockHeight prometheus.Gauge + Rank *prometheus.GaugeVec + ActiveSet prometheus.Gauge ValidatedBlocks *prometheus.CounterVec MissedBlocks *prometheus.CounterVec TrackedBlocks prometheus.Counter @@ -27,6 +29,21 @@ func New(namespace string) *Metrics { Help: "Latest known block height (all nodes mixed up)", }, ), + Rank: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "rank", + Help: "Rank of the validator (or 0 is not bonded)", + }, + []string{"address", "name"}, + ), + ActiveSet: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "active_set", + Help: "Number of validators in the active set", + }, + ), ValidatedBlocks: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, @@ -100,6 +117,8 @@ func New(namespace string) *Metrics { } prometheus.MustRegister(metrics.BlockHeight) + prometheus.MustRegister(metrics.Rank) + prometheus.MustRegister(metrics.ActiveSet) prometheus.MustRegister(metrics.ValidatedBlocks) prometheus.MustRegister(metrics.MissedBlocks) prometheus.MustRegister(metrics.TrackedBlocks)