Skip to content

Commit

Permalink
Migrate subnet-evm specific files back to metrics/prometheus
Browse files Browse the repository at this point in the history
- Bring over refactoring and fixes done in ava-labs/libevm#103
- Bring over test refactoring done in ava-labs/libevm#103
  • Loading branch information
qdm12 committed Jan 13, 2025
1 parent b63a56c commit 8c8514c
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 6 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/VictoriaMetrics/fastcache v1.12.1
github.com/antithesishq/antithesis-sdk-go v0.3.8
github.com/ava-labs/avalanchego v1.12.1
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1
github.com/cespare/cp v0.1.0
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/davecgh/go-spew v1.1.1
Expand All @@ -31,6 +31,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo/v2 v2.13.1
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/client_model v0.3.0
github.com/spf13/cast v1.5.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.12.0
Expand Down Expand Up @@ -128,7 +129,6 @@ require (
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ github.com/ava-labs/avalanchego v1.12.1 h1:NL04K5+gciC2XqGZbDcIu0nuVApEddzc6Yyuj
github.com/ava-labs/avalanchego v1.12.1/go.mod h1:xnVvN86jhxndxfS8e0U7v/0woyfx9BhX/feld7XDjDE=
github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer h1:mRB03tLPUvgNko4nP4VwWQdiHeHaLHtdwsnqwxrsGec=
github.com/ava-labs/coreth v0.13.9-rc.2-encapsulate-signer/go.mod h1:tqRAe+7bGLo2Rq/Ph4iYMSch72ag/Jn0DiDMDz1Xa9E=
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f h1:KeKggoIyyF+o/GeGofo2+UO93WN7ulqMcVlP2K3iUzM=
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f/go.mod h1:M8TCw2g1D5GBB7hu7g1F4aot5bRHGSxnBawNVmHE9Z0=
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 h1:ughW0E2DUNRnvwJYNU8zUSCUzIWdcOwyXSBpy7oauZE=
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1/go.mod h1:yBctIV/wnxXTF38h95943jvpuk4aj07TrjbpoGor6LQ=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
Expand Down
10 changes: 10 additions & 0 deletions metrics/prometheus/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// (c) 2025 Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package prometheus

type Registry interface {
// Call the given function for each registered metric.
Each(func(string, any))
// Get the metric by the given name or nil if none is registered.
Get(string) any
}
193 changes: 193 additions & 0 deletions metrics/prometheus/prometheus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// (c) 2025 Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package prometheus

import (
"errors"
"fmt"
"sort"
"strings"

"github.com/prometheus/client_golang/prometheus"

"github.com/ava-labs/libevm/metrics"

dto "github.com/prometheus/client_model/go"
)

type Gatherer struct {
registry Registry
}

var _ prometheus.Gatherer = (*Gatherer)(nil)

// NewGatherer returns a gatherer using the given registry.
// Note this gatherer implements the [prometheus.Gatherer] interface.
func NewGatherer(registry Registry) *Gatherer {
return &Gatherer{
registry: registry,
}
}

func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) {
// Gather and pre-sort the metrics to avoid random listings
var names []string
g.registry.Each(func(name string, i any) {
names = append(names, name)
})
sort.Strings(names)

mfs = make([]*dto.MetricFamily, 0, len(names))
for _, name := range names {
mf, err := metricFamily(g.registry, name)
if errors.Is(err, errMetricSkip) {
continue
}
mfs = append(mfs, mf)
}

return mfs, nil
}

var (
errMetricSkip = errors.New("metric skipped")
)

func ptrTo[T any](x T) *T { return &x }

func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err error) {
metric := registry.Get(name)
name = strings.ReplaceAll(name, "/", "_")

switch m := metric.(type) {
case metrics.Counter:
return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_COUNTER.Enum(),
Metric: []*dto.Metric{{
Counter: &dto.Counter{
Value: ptrTo(float64(m.Snapshot().Count())),
},
}},
}, nil
case metrics.CounterFloat64:
return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_COUNTER.Enum(),
Metric: []*dto.Metric{{
Counter: &dto.Counter{
Value: ptrTo(m.Snapshot().Count()),
},
}},
}, nil
case metrics.Gauge:
return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_GAUGE.Enum(),
Metric: []*dto.Metric{{
Gauge: &dto.Gauge{
Value: ptrTo(float64(m.Snapshot().Value())),
},
}},
}, nil
case metrics.GaugeFloat64:
return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_GAUGE.Enum(),
Metric: []*dto.Metric{{
Gauge: &dto.Gauge{
Value: ptrTo(m.Snapshot().Value()),
},
}},
}, nil
case metrics.Histogram:
snapshot := m.Snapshot()

quantiles := []float64{.5, .75, .95, .99, .999, .9999}
thresholds := snapshot.Percentiles(quantiles)
dtoQuantiles := make([]*dto.Quantile, len(quantiles))
for i := range thresholds {
dtoQuantiles[i] = &dto.Quantile{
Quantile: ptrTo(quantiles[i]),
Value: ptrTo(thresholds[i]),
}
}

return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_SUMMARY.Enum(),
Metric: []*dto.Metric{{
Summary: &dto.Summary{
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
SampleSum: ptrTo(float64(snapshot.Sum())),
Quantile: dtoQuantiles,
},
}},
}, nil
case metrics.Meter:
return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_GAUGE.Enum(),
Metric: []*dto.Metric{{
Gauge: &dto.Gauge{
Value: ptrTo(float64(m.Snapshot().Count())),
},
}},
}, nil
case metrics.Timer:
snapshot := m.Snapshot()

quantiles := []float64{.5, .75, .95, .99, .999, .9999}
thresholds := snapshot.Percentiles(quantiles)
dtoQuantiles := make([]*dto.Quantile, len(quantiles))
for i := range thresholds {
dtoQuantiles[i] = &dto.Quantile{
Quantile: ptrTo(quantiles[i]),
Value: ptrTo(thresholds[i]),
}
}

return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_SUMMARY.Enum(),
Metric: []*dto.Metric{{
Summary: &dto.Summary{
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
SampleSum: ptrTo(float64(snapshot.Sum())),
Quantile: dtoQuantiles,
},
}},
}, nil
case metrics.ResettingTimer:
snapshot := m.Snapshot()
if snapshot.Count() == 0 {
return nil, fmt.Errorf("%w: resetting timer metric count is zero", errMetricSkip)
}

pvShortPercent := []float64{50, 95, 99}
thresholds := snapshot.Percentiles(pvShortPercent)
dtoQuantiles := make([]*dto.Quantile, len(pvShortPercent))
for i := range pvShortPercent {
dtoQuantiles[i] = &dto.Quantile{
Quantile: ptrTo(pvShortPercent[i]),
Value: ptrTo(thresholds[i]),
}
}

return &dto.MetricFamily{
Name: &name,
Type: dto.MetricType_SUMMARY.Enum(),
Metric: []*dto.Metric{{
Summary: &dto.Summary{
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
// TODO: do we need to specify SampleSum here? and if so
// what should that be?
Quantile: dtoQuantiles,
},
}},
}, nil
default:
return nil, fmt.Errorf("metric type is not supported: %T", metric)
}
}
91 changes: 91 additions & 0 deletions metrics/prometheus/prometheus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// (c) 2025 Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package prometheus

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ava-labs/libevm/metrics"
)

func TestGatherer_Gather(t *testing.T) {
metricsEnabled := metrics.Enabled
if !metricsEnabled {
metrics.Enabled = true
t.Cleanup(func() {
metrics.Enabled = false
})
}

registry := metrics.NewRegistry()
register := func(t *testing.T, name string, collector any) {
t.Helper()
err := registry.Register(name, collector)
require.NoError(t, err)
}

counter := metrics.NewCounter()
counter.Inc(12345)
register(t, "test/counter", counter)

gauge := metrics.NewGauge()
gauge.Update(23456)
register(t, "test/gauge", gauge)

gaugeFloat64 := metrics.NewGaugeFloat64()
gaugeFloat64.Update(34567.89)
register(t, "test/gauge_float64", gaugeFloat64)

sample := metrics.NewUniformSample(1028)
histogram := metrics.NewHistogram(sample)
register(t, "test/histogram", histogram)

meter := metrics.NewMeter()
t.Cleanup(meter.Stop)
meter.Mark(9999999)
register(t, "test/meter", meter)

timer := metrics.NewTimer()
t.Cleanup(timer.Stop)
timer.Update(20 * time.Millisecond)
timer.Update(21 * time.Millisecond)
timer.Update(22 * time.Millisecond)
timer.Update(120 * time.Millisecond)
timer.Update(23 * time.Millisecond)
timer.Update(24 * time.Millisecond)
register(t, "test/timer", timer)

resettingTimer := metrics.NewResettingTimer()
register(t, "test/resetting_timer", resettingTimer)
resettingTimer.Update(time.Second) // must be after register call

emptyResettingTimer := metrics.NewResettingTimer()
register(t, "test/empty_resetting_timer", emptyResettingTimer)

emptyResettingTimer.Update(time.Second) // no effect because of snapshot below
register(t, "test/empty_resetting_timer_snapshot", emptyResettingTimer.Snapshot())

g := NewGatherer(registry)

families, err := g.Gather()
require.NoError(t, err)
familyStrings := make([]string, len(families))
for i := range families {
familyStrings[i] = families[i].String()
}
want := []string{
`name:"test_counter" type:COUNTER metric:<counter:<value:12345 > > `,
`name:"test_gauge" type:GAUGE metric:<gauge:<value:23456 > > `,
`name:"test_gauge_float64" type:GAUGE metric:<gauge:<value:34567.89 > > `,
`name:"test_histogram" type:SUMMARY metric:<summary:<sample_count:0 sample_sum:0 quantile:<quantile:0.5 value:0 > quantile:<quantile:0.75 value:0 > quantile:<quantile:0.95 value:0 > quantile:<quantile:0.99 value:0 > quantile:<quantile:0.999 value:0 > quantile:<quantile:0.9999 value:0 > > > `,
`name:"test_meter" type:GAUGE metric:<gauge:<value:9.999999e+06 > > `,
`name:"test_resetting_timer" type:SUMMARY metric:<summary:<sample_count:1 quantile:<quantile:50 value:1e+09 > quantile:<quantile:95 value:1e+09 > quantile:<quantile:99 value:1e+09 > > > `,
`name:"test_timer" type:SUMMARY metric:<summary:<sample_count:6 sample_sum:2.3e+08 quantile:<quantile:0.5 value:2.25e+07 > quantile:<quantile:0.75 value:4.8e+07 > quantile:<quantile:0.95 value:1.2e+08 > quantile:<quantile:0.99 value:1.2e+08 > quantile:<quantile:0.999 value:1.2e+08 > quantile:<quantile:0.9999 value:1.2e+08 > > > `,
}
assert.Equal(t, want, familyStrings)
}
4 changes: 2 additions & 2 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/prometheus/client_golang/prometheus"

"github.com/ava-labs/libevm/metrics"
libevmprometheus "github.com/ava-labs/libevm/metrics/prometheus"
"github.com/ava-labs/subnet-evm/commontype"
"github.com/ava-labs/subnet-evm/consensus/dummy"
"github.com/ava-labs/subnet-evm/constants"
Expand All @@ -34,6 +33,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/eth"
"github.com/ava-labs/subnet-evm/eth/ethconfig"
subnetevmprometheus "github.com/ava-labs/subnet-evm/metrics/prometheus"
"github.com/ava-labs/subnet-evm/miner"
"github.com/ava-labs/subnet-evm/node"
"github.com/ava-labs/subnet-evm/params"
Expand Down Expand Up @@ -555,7 +555,7 @@ func (vm *VM) initializeMetrics() error {
return nil
}

gatherer := libevmprometheus.NewGatherer(metrics.DefaultRegistry)
gatherer := subnetevmprometheus.NewGatherer(metrics.DefaultRegistry)
if err := vm.ctx.Metrics.Register(ethMetricsPrefix, gatherer); err != nil {
return err
}
Expand Down

0 comments on commit 8c8514c

Please sign in to comment.