Skip to content

Commit 441ed1e

Browse files
committed
Migrate subnet-evm specific files back to metrics/prometheus
- Bring over refactoring and fixes done in ava-labs/libevm#103 - Bring over test refactoring done in ava-labs/libevm#103
1 parent 11a073e commit 441ed1e

File tree

6 files changed

+300
-6
lines changed

6 files changed

+300
-6
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/VictoriaMetrics/fastcache v1.12.1
77
github.com/antithesishq/antithesis-sdk-go v0.3.8
88
github.com/ava-labs/avalanchego v1.12.3-rc.1
9-
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f
9+
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1
1010
github.com/cespare/cp v0.1.0
1111
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
1212
github.com/davecgh/go-spew v1.1.1
@@ -31,6 +31,7 @@ require (
3131
github.com/olekukonko/tablewriter v0.0.5
3232
github.com/onsi/ginkgo/v2 v2.13.1
3333
github.com/prometheus/client_golang v1.16.0
34+
github.com/prometheus/client_model v0.3.0
3435
github.com/spf13/cast v1.5.0
3536
github.com/spf13/pflag v1.0.5
3637
github.com/spf13/viper v1.12.0
@@ -126,7 +127,6 @@ require (
126127
github.com/pires/go-proxyproto v0.6.2 // indirect
127128
github.com/pkg/errors v0.9.1 // indirect
128129
github.com/pmezard/go-difflib v1.0.0 // indirect
129-
github.com/prometheus/client_model v0.3.0 // indirect
130130
github.com/prometheus/common v0.42.0 // indirect
131131
github.com/prometheus/procfs v0.10.1 // indirect
132132
github.com/rivo/uniseg v0.2.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ github.com/ava-labs/avalanchego v1.12.3-rc.1 h1:p56A8bzbSCzz1OvXkIuGSyHsCj5MAzRM
6666
github.com/ava-labs/avalanchego v1.12.3-rc.1/go.mod h1:odvg1kVIdtPBFE5LRzwdPhGC16TnkOGVO4k8sZlDs6E=
6767
github.com/ava-labs/coreth v0.14.1-rc.3 h1:4MXKCgW7kUuKsssRiN9Pl8hWFRKuanD13/v1OtpQJPw=
6868
github.com/ava-labs/coreth v0.14.1-rc.3/go.mod h1:gIGr+5WDNX1DrFvUMy53AtTpkxlM/8cNOD/PDIChKfM=
69-
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f h1:KeKggoIyyF+o/GeGofo2+UO93WN7ulqMcVlP2K3iUzM=
70-
github.com/ava-labs/libevm v0.0.0-20250113110843-18c93de8be7f/go.mod h1:M8TCw2g1D5GBB7hu7g1F4aot5bRHGSxnBawNVmHE9Z0=
69+
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1 h1:ughW0E2DUNRnvwJYNU8zUSCUzIWdcOwyXSBpy7oauZE=
70+
github.com/ava-labs/libevm v1.13.14-0.1.0-rc.1/go.mod h1:yBctIV/wnxXTF38h95943jvpuk4aj07TrjbpoGor6LQ=
7171
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
7272
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
7373
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=

metrics/prometheus/interfaces.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// (c) 2025 Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
package prometheus
4+
5+
type Registry interface {
6+
// Call the given function for each registered metric.
7+
Each(func(string, any))
8+
// Get the metric by the given name or nil if none is registered.
9+
Get(string) any
10+
}

metrics/prometheus/prometheus.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// (c) 2025 Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package prometheus
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"sort"
10+
"strings"
11+
12+
"github.com/prometheus/client_golang/prometheus"
13+
14+
"github.com/ava-labs/libevm/metrics"
15+
16+
dto "github.com/prometheus/client_model/go"
17+
)
18+
19+
type Gatherer struct {
20+
registry Registry
21+
}
22+
23+
var _ prometheus.Gatherer = (*Gatherer)(nil)
24+
25+
// NewGatherer returns a gatherer using the given registry.
26+
// Note this gatherer implements the [prometheus.Gatherer] interface.
27+
func NewGatherer(registry Registry) *Gatherer {
28+
return &Gatherer{
29+
registry: registry,
30+
}
31+
}
32+
33+
func (g *Gatherer) Gather() (mfs []*dto.MetricFamily, err error) {
34+
// Gather and pre-sort the metrics to avoid random listings
35+
var names []string
36+
g.registry.Each(func(name string, i any) {
37+
names = append(names, name)
38+
})
39+
sort.Strings(names)
40+
41+
mfs = make([]*dto.MetricFamily, 0, len(names))
42+
for _, name := range names {
43+
mf, err := metricFamily(g.registry, name)
44+
if errors.Is(err, errMetricSkip) {
45+
continue
46+
}
47+
mfs = append(mfs, mf)
48+
}
49+
50+
return mfs, nil
51+
}
52+
53+
var (
54+
errMetricSkip = errors.New("metric skipped")
55+
)
56+
57+
func ptrTo[T any](x T) *T { return &x }
58+
59+
func metricFamily(registry Registry, name string) (mf *dto.MetricFamily, err error) {
60+
metric := registry.Get(name)
61+
name = strings.ReplaceAll(name, "/", "_")
62+
63+
switch m := metric.(type) {
64+
case metrics.Counter:
65+
return &dto.MetricFamily{
66+
Name: &name,
67+
Type: dto.MetricType_COUNTER.Enum(),
68+
Metric: []*dto.Metric{{
69+
Counter: &dto.Counter{
70+
Value: ptrTo(float64(m.Snapshot().Count())),
71+
},
72+
}},
73+
}, nil
74+
case metrics.CounterFloat64:
75+
return &dto.MetricFamily{
76+
Name: &name,
77+
Type: dto.MetricType_COUNTER.Enum(),
78+
Metric: []*dto.Metric{{
79+
Counter: &dto.Counter{
80+
Value: ptrTo(m.Snapshot().Count()),
81+
},
82+
}},
83+
}, nil
84+
case metrics.Gauge:
85+
return &dto.MetricFamily{
86+
Name: &name,
87+
Type: dto.MetricType_GAUGE.Enum(),
88+
Metric: []*dto.Metric{{
89+
Gauge: &dto.Gauge{
90+
Value: ptrTo(float64(m.Snapshot().Value())),
91+
},
92+
}},
93+
}, nil
94+
case metrics.GaugeFloat64:
95+
return &dto.MetricFamily{
96+
Name: &name,
97+
Type: dto.MetricType_GAUGE.Enum(),
98+
Metric: []*dto.Metric{{
99+
Gauge: &dto.Gauge{
100+
Value: ptrTo(m.Snapshot().Value()),
101+
},
102+
}},
103+
}, nil
104+
case metrics.Histogram:
105+
snapshot := m.Snapshot()
106+
107+
quantiles := []float64{.5, .75, .95, .99, .999, .9999}
108+
thresholds := snapshot.Percentiles(quantiles)
109+
dtoQuantiles := make([]*dto.Quantile, len(quantiles))
110+
for i := range thresholds {
111+
dtoQuantiles[i] = &dto.Quantile{
112+
Quantile: ptrTo(quantiles[i]),
113+
Value: ptrTo(thresholds[i]),
114+
}
115+
}
116+
117+
return &dto.MetricFamily{
118+
Name: &name,
119+
Type: dto.MetricType_SUMMARY.Enum(),
120+
Metric: []*dto.Metric{{
121+
Summary: &dto.Summary{
122+
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
123+
SampleSum: ptrTo(float64(snapshot.Sum())),
124+
Quantile: dtoQuantiles,
125+
},
126+
}},
127+
}, nil
128+
case metrics.Meter:
129+
return &dto.MetricFamily{
130+
Name: &name,
131+
Type: dto.MetricType_GAUGE.Enum(),
132+
Metric: []*dto.Metric{{
133+
Gauge: &dto.Gauge{
134+
Value: ptrTo(float64(m.Snapshot().Count())),
135+
},
136+
}},
137+
}, nil
138+
case metrics.Timer:
139+
snapshot := m.Snapshot()
140+
141+
quantiles := []float64{.5, .75, .95, .99, .999, .9999}
142+
thresholds := snapshot.Percentiles(quantiles)
143+
dtoQuantiles := make([]*dto.Quantile, len(quantiles))
144+
for i := range thresholds {
145+
dtoQuantiles[i] = &dto.Quantile{
146+
Quantile: ptrTo(quantiles[i]),
147+
Value: ptrTo(thresholds[i]),
148+
}
149+
}
150+
151+
return &dto.MetricFamily{
152+
Name: &name,
153+
Type: dto.MetricType_SUMMARY.Enum(),
154+
Metric: []*dto.Metric{{
155+
Summary: &dto.Summary{
156+
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
157+
SampleSum: ptrTo(float64(snapshot.Sum())),
158+
Quantile: dtoQuantiles,
159+
},
160+
}},
161+
}, nil
162+
case metrics.ResettingTimer:
163+
snapshot := m.Snapshot()
164+
if snapshot.Count() == 0 {
165+
return nil, fmt.Errorf("%w: resetting timer metric count is zero", errMetricSkip)
166+
}
167+
168+
pvShortPercent := []float64{50, 95, 99}
169+
thresholds := snapshot.Percentiles(pvShortPercent)
170+
dtoQuantiles := make([]*dto.Quantile, len(pvShortPercent))
171+
for i := range pvShortPercent {
172+
dtoQuantiles[i] = &dto.Quantile{
173+
Quantile: ptrTo(pvShortPercent[i]),
174+
Value: ptrTo(thresholds[i]),
175+
}
176+
}
177+
178+
return &dto.MetricFamily{
179+
Name: &name,
180+
Type: dto.MetricType_SUMMARY.Enum(),
181+
Metric: []*dto.Metric{{
182+
Summary: &dto.Summary{
183+
SampleCount: ptrTo(uint64(snapshot.Count())), //nolint:gosec
184+
// TODO: do we need to specify SampleSum here? and if so
185+
// what should that be?
186+
Quantile: dtoQuantiles,
187+
},
188+
}},
189+
}, nil
190+
default:
191+
return nil, fmt.Errorf("metric type is not supported: %T", metric)
192+
}
193+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// (c) 2025 Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package prometheus
5+
6+
import (
7+
"testing"
8+
"time"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/ava-labs/libevm/metrics"
14+
)
15+
16+
func TestGatherer_Gather(t *testing.T) {
17+
metricsEnabled := metrics.Enabled
18+
if !metricsEnabled {
19+
metrics.Enabled = true
20+
t.Cleanup(func() {
21+
metrics.Enabled = false
22+
})
23+
}
24+
25+
registry := metrics.NewRegistry()
26+
register := func(t *testing.T, name string, collector any) {
27+
t.Helper()
28+
err := registry.Register(name, collector)
29+
require.NoError(t, err)
30+
}
31+
32+
counter := metrics.NewCounter()
33+
counter.Inc(12345)
34+
register(t, "test/counter", counter)
35+
36+
gauge := metrics.NewGauge()
37+
gauge.Update(23456)
38+
register(t, "test/gauge", gauge)
39+
40+
gaugeFloat64 := metrics.NewGaugeFloat64()
41+
gaugeFloat64.Update(34567.89)
42+
register(t, "test/gauge_float64", gaugeFloat64)
43+
44+
sample := metrics.NewUniformSample(1028)
45+
histogram := metrics.NewHistogram(sample)
46+
register(t, "test/histogram", histogram)
47+
48+
meter := metrics.NewMeter()
49+
t.Cleanup(meter.Stop)
50+
meter.Mark(9999999)
51+
register(t, "test/meter", meter)
52+
53+
timer := metrics.NewTimer()
54+
t.Cleanup(timer.Stop)
55+
timer.Update(20 * time.Millisecond)
56+
timer.Update(21 * time.Millisecond)
57+
timer.Update(22 * time.Millisecond)
58+
timer.Update(120 * time.Millisecond)
59+
timer.Update(23 * time.Millisecond)
60+
timer.Update(24 * time.Millisecond)
61+
register(t, "test/timer", timer)
62+
63+
resettingTimer := metrics.NewResettingTimer()
64+
register(t, "test/resetting_timer", resettingTimer)
65+
resettingTimer.Update(time.Second) // must be after register call
66+
67+
emptyResettingTimer := metrics.NewResettingTimer()
68+
register(t, "test/empty_resetting_timer", emptyResettingTimer)
69+
70+
emptyResettingTimer.Update(time.Second) // no effect because of snapshot below
71+
register(t, "test/empty_resetting_timer_snapshot", emptyResettingTimer.Snapshot())
72+
73+
g := NewGatherer(registry)
74+
75+
families, err := g.Gather()
76+
require.NoError(t, err)
77+
familyStrings := make([]string, len(families))
78+
for i := range families {
79+
familyStrings[i] = families[i].String()
80+
}
81+
want := []string{
82+
`name:"test_counter" type:COUNTER metric:<counter:<value:12345 > > `,
83+
`name:"test_gauge" type:GAUGE metric:<gauge:<value:23456 > > `,
84+
`name:"test_gauge_float64" type:GAUGE metric:<gauge:<value:34567.89 > > `,
85+
`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 > > > `,
86+
`name:"test_meter" type:GAUGE metric:<gauge:<value:9.999999e+06 > > `,
87+
`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 > > > `,
88+
`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 > > > `,
89+
}
90+
assert.Equal(t, want, familyStrings)
91+
}

plugin/evm/vm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"github.com/prometheus/client_golang/prometheus"
2525

2626
"github.com/ava-labs/libevm/metrics"
27-
libevmprometheus "github.com/ava-labs/libevm/metrics/prometheus"
2827
"github.com/ava-labs/subnet-evm/commontype"
2928
"github.com/ava-labs/subnet-evm/consensus/dummy"
3029
"github.com/ava-labs/subnet-evm/constants"
@@ -34,6 +33,7 @@ import (
3433
"github.com/ava-labs/subnet-evm/core/types"
3534
"github.com/ava-labs/subnet-evm/eth"
3635
"github.com/ava-labs/subnet-evm/eth/ethconfig"
36+
subnetevmprometheus "github.com/ava-labs/subnet-evm/metrics/prometheus"
3737
"github.com/ava-labs/subnet-evm/miner"
3838
"github.com/ava-labs/subnet-evm/node"
3939
"github.com/ava-labs/subnet-evm/params"
@@ -560,7 +560,7 @@ func (vm *VM) initializeMetrics() error {
560560
return nil
561561
}
562562

563-
gatherer := libevmprometheus.NewGatherer(metrics.DefaultRegistry)
563+
gatherer := subnetevmprometheus.NewGatherer(metrics.DefaultRegistry)
564564
if err := vm.ctx.Metrics.Register(ethMetricsPrefix, gatherer); err != nil {
565565
return err
566566
}

0 commit comments

Comments
 (0)