Skip to content

Commit eb41f10

Browse files
Prometheus Metrics (#832)
* add prometheus setup * add beacon node metrics * add metrics for relay header * add relay latency metric * add relays status code metrics * fix lint * fix lint * add prometheus setup * add some metrics * fix lint * upd readme * rem line break * fix spelling * port to victoria metrics --------- Co-authored-by: Bharath Vedartham <[email protected]>
1 parent 5cce13c commit eb41f10

File tree

10 files changed

+177
-8
lines changed

10 files changed

+177
-8
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ Usage of mev-boost:
270270
use Sepolia
271271
-version
272272
only print version
273+
-metrics
274+
enables a metrics server (default: false)
275+
-metrics-addr string
276+
listening address for the metrics server (default: "localhost:18551")
273277
```
274278

275279
### `-relays` vs `-relay`
@@ -309,6 +313,10 @@ Example for setting a minimum bid value of 0.06 ETH:
309313
-relay $YOUR_RELAY_CHOICE_C
310314
```
311315

316+
### Enabling metrics
317+
318+
Optionally, the `-metrics` flag can be provided to expose a prometheus metrics server. The metrics server address/port can be changed with the `-metrics-addr` (e.g., `-metrics-addr localhost:9009`) flag.
319+
312320
---
313321

314322
# API

cli/flags.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const (
77
GenesisCategory = "GENESIS"
88
RelayCategory = "RELAYS"
99
GeneralCategory = "GENERAL"
10+
Metrics = "METRICS"
1011
)
1112

1213
var flags = []cli.Flag{
@@ -35,6 +36,10 @@ var flags = []cli.Flag{
3536
timeoutGetPayloadFlag,
3637
timeoutRegValFlag,
3738
maxRetriesFlag,
39+
40+
// metrics
41+
metricsFlag,
42+
metricsAddrFlag,
3843
}
3944

4045
var (
@@ -178,4 +183,19 @@ var (
178183
Value: 5,
179184
Category: RelayCategory,
180185
}
186+
187+
// metrics
188+
metricsFlag = &cli.BoolFlag{
189+
Name: "metrics",
190+
Sources: cli.EnvVars("METRICS_ENABLED"),
191+
Usage: "enables a metrics server",
192+
Category: Metrics,
193+
}
194+
metricsAddrFlag = &cli.StringFlag{
195+
Name: "metrics-addr",
196+
Sources: cli.EnvVars("METRICS_ADDR"),
197+
Value: "localhost:18551",
198+
Usage: "listening address for the metrics server",
199+
Category: Metrics,
200+
}
181201
)

cli/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ func start(_ context.Context, cmd *cli.Command) error {
6868
genesisForkVersion, genesisTime = setupGenesis(cmd)
6969
relays, minBid, relayCheck = setupRelays(cmd)
7070
listenAddr = cmd.String(addrFlag.Name)
71+
metricsEnabled = cmd.Bool(metricsFlag.Name)
72+
metricsAddr = cmd.String(metricsAddrFlag.Name)
7173
)
7274

7375
opts := server.BoostServiceOpts{
@@ -82,6 +84,7 @@ func start(_ context.Context, cmd *cli.Command) error {
8284
RequestTimeoutGetPayload: time.Duration(cmd.Int(timeoutGetPayloadFlag.Name)) * time.Millisecond,
8385
RequestTimeoutRegVal: time.Duration(cmd.Int(timeoutRegValFlag.Name)) * time.Millisecond,
8486
RequestMaxRetries: cmd.Int(maxRetriesFlag.Name),
87+
MetricsAddr: metricsAddr,
8588
}
8689
service, err := server.NewBoostService(opts)
8790
if err != nil {
@@ -92,6 +95,15 @@ func start(_ context.Context, cmd *cli.Command) error {
9295
log.Error("no relay passed the health-check!")
9396
}
9497

98+
if metricsEnabled {
99+
go func() {
100+
log.Infof("metrics server listening on %v", opts.MetricsAddr)
101+
if err := service.StartMetricsServer(); err != nil {
102+
log.WithError(err).Error("metrics server exited with error")
103+
}
104+
}()
105+
}
106+
95107
log.Infof("listening on %v", listenAddr)
96108
return service.StartHTTPServer()
97109
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/flashbots/mev-boost
33
go 1.24.0
44

55
require (
6+
github.com/VictoriaMetrics/metrics v1.40.1
67
github.com/ethereum/go-ethereum v1.15.9
78
github.com/flashbots/go-boost-utils v1.9.0
89
github.com/flashbots/go-utils v0.10.0
@@ -30,6 +31,8 @@ require (
3031
github.com/mmcloughlin/addchain v0.4.0 // indirect
3132
github.com/rivo/uniseg v0.4.7 // indirect
3233
github.com/supranational/blst v0.3.14 // indirect
34+
github.com/valyala/fastrand v1.1.0 // indirect
35+
github.com/valyala/histogram v1.2.0 // indirect
3336
github.com/yusufpapurcu/wmi v1.2.4 // indirect
3437
golang.org/x/sync v0.13.0 // indirect
3538
rsc.io/tmplfunc v0.0.3 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
22
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
3+
github.com/VictoriaMetrics/metrics v1.40.1 h1:FrF5uJRpIVj9fayWcn8xgiI+FYsKGMslzPuOXjdeyR4=
4+
github.com/VictoriaMetrics/metrics v1.40.1/go.mod h1:XE4uudAAIRaJE614Tl5HMrtoEU6+GDZO4QTnNSsZRuA=
35
github.com/attestantio/go-builder-client v0.7.2 h1:bOrtysEIZd9bEM+mAeT6OtAo6LSAft/qylBLwFoFwZ0=
46
github.com/attestantio/go-builder-client v0.7.2/go.mod h1:+NADxbaknI5yxl+0mCkMa/VciVsesxRMGNP/poDfV08=
57
github.com/attestantio/go-eth2-client v0.27.1 h1:g7bm+gG/p+gfzYdEuxuAepVWYb8EO+2KojV5/Lo2BxM=
@@ -114,6 +116,10 @@ github.com/trailofbits/go-fuzz-utils v0.0.0-20240830175354-474de707d2aa h1:jXdW8
114116
github.com/trailofbits/go-fuzz-utils v0.0.0-20240830175354-474de707d2aa/go.mod h1:/7KgvY5ghyUsjocUh9dMkLCwKtNxqe0kWl5SIdpLtO8=
115117
github.com/urfave/cli/v3 v3.2.0 h1:m8WIXY0U9LCuUl5r+0fqLWDhNYWt6qvlW+GcF4EoXf8=
116118
github.com/urfave/cli/v3 v3.2.0/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
119+
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
120+
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
121+
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
122+
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
117123
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
118124
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
119125
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=

server/get_header.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/attestantio/go-eth2-client/spec"
2020
"github.com/attestantio/go-eth2-client/spec/phase0"
2121
"github.com/flashbots/mev-boost/config"
22+
"github.com/flashbots/mev-boost/server/params"
2223
"github.com/flashbots/mev-boost/server/types"
2324
"github.com/google/uuid"
2425
"github.com/sirupsen/logrus"
@@ -94,13 +95,16 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa
9495

9596
// Send the request
9697
log.Debug("requesting header")
98+
start := time.Now()
9799
resp, err := m.httpClientGetHeader.Do(req)
100+
RecordRelayLatency(params.PathGetHeader, relay.String(), float64(time.Since(start).Microseconds()))
98101
if err != nil {
99102
log.WithError(err).Warn("error calling getHeader on relay")
100103
return
101104
}
102105
defer resp.Body.Close()
103106

107+
RecordRelayStatusCode(strconv.Itoa(resp.StatusCode), params.PathGetHeader, relay.String())
104108
// Check if no header is available
105109
if resp.StatusCode == http.StatusNoContent {
106110
log.Debug("no-content response")
@@ -207,9 +211,15 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa
207211

208212
log.Debug("bid received")
209213

214+
RecordRelayLastSlot(relay.String(), uint64(slot))
215+
216+
valueEthFloat64, _ := valueEth.Float64()
217+
RecordBidValue(relay.String(), valueEthFloat64)
218+
210219
// Skip if value is lower than the minimum bid
211220
if bidInfo.value.CmpBig(m.relayMinBid.BigInt()) == -1 {
212221
log.Debug("ignoring bid below min-bid value")
222+
IncrementBidBelowMinBid(relay.String())
213223
return
214224
}
215225

@@ -248,13 +258,18 @@ func (m *BoostService) getHeader(log *logrus.Entry, slot phase0.Slot, pubkey, pa
248258
log.Debug("new best bid")
249259
result.response = *bid
250260
result.bidInfo = bidInfo
261+
251262
result.t = time.Now()
252263
}(relay)
253264
}
254265
wg.Wait()
255-
256266
// Set the winning relays before returning
257267
result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())]
268+
269+
if len(result.relays) > 0 {
270+
RecordWinningBidValue(result.bidInfo.value.Float64())
271+
}
272+
258273
return result, nil
259274
}
260275

server/get_payload.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"mime"
1111
"net/http"
1212
"slices"
13+
"strconv"
1314
"sync/atomic"
1415
"time"
1516

@@ -212,20 +213,23 @@ func (m *BoostService) innerGetPayload(log *logrus.Entry, signedBlindedBeaconBlo
212213
req.Header.Set(HeaderDateMilliseconds, fmt.Sprintf("%d", time.Now().UTC().UnixMilli()))
213214
req.Header.Set(HeaderUserAgent, userAgent)
214215

215-
// Send the request
216+
statusCode := http.StatusOK
217+
endpoint := params.PathGetPayload
218+
if version == GetPayloadV2 {
219+
statusCode = http.StatusAccepted
220+
endpoint = params.PathGetPayloadV2
221+
}
222+
// Send the request and record latency
216223
log.Debug("submitting signed blinded block")
224+
start := time.Now()
217225
resp, err := m.httpClientGetPayload.Do(req)
226+
RecordRelayLatency(endpoint, relay.String(), float64(time.Since(start).Microseconds()))
218227
if err != nil {
219228
log.WithError(err).Warnf("error calling getPayload%s on relay", version)
220229
return nil, err
221230
}
222231

223-
var statusCode int
224-
if version == GetPayloadV1 {
225-
statusCode = http.StatusOK
226-
} else {
227-
statusCode = http.StatusAccepted
228-
}
232+
RecordRelayStatusCode(strconv.Itoa(statusCode), endpoint, relay.String())
229233
// Check that the response was successful
230234
if resp.StatusCode != statusCode {
231235
err = fmt.Errorf("%w: %d", errHTTPErrorResponse, resp.StatusCode)

server/metrics.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package server
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/VictoriaMetrics/metrics"
7+
)
8+
9+
var winningBidValue = metrics.NewHistogram("mev_boost_winning_bid_value")
10+
11+
const (
12+
beaconNodeStatusLabel = `mev_boost_beacon_node_status_code_total{http_status_code="%s",endpoint="%s"}`
13+
bidValuesLabel = `mev_boost_bid_values{relay="%s"}`
14+
bidsBelowMinBidLabel = `mev_boost_bids_below_min_bid_total{relay="%s"}`
15+
relayLatencyLabel = `mev_boost_relay_latency{endpoint="%s",relay="%s"}`
16+
relayStatusCodeLabel = `mev_boost_relay_status_code_total{http_status_code="%s",endpoint="%s",relay="%s"}`
17+
relayLastSlotLabel = `mev_boost_relay_last_slot{relay="%s"}`
18+
msIntoSlotLabel = `mev_boost_millisec_into_slot{endpoint="%s"}`
19+
)
20+
21+
func IncrementBeaconNodeStatus(status, endpoint string) {
22+
l := fmt.Sprintf(beaconNodeStatusLabel, status, endpoint)
23+
metrics.GetOrCreateCounter(l).Inc()
24+
}
25+
26+
func RecordBidValue(relay string, value float64) {
27+
l := fmt.Sprintf(bidValuesLabel, relay)
28+
metrics.GetOrCreateHistogram(l).Update(value)
29+
}
30+
31+
func IncrementBidBelowMinBid(relay string) {
32+
l := fmt.Sprintf(bidsBelowMinBidLabel, relay)
33+
metrics.GetOrCreateCounter(l).Inc()
34+
}
35+
36+
func RecordWinningBidValue(value float64) {
37+
winningBidValue.Update(value)
38+
}
39+
40+
func RecordRelayLatency(endpoint, relay string, latency float64) {
41+
l := fmt.Sprintf(relayLatencyLabel, endpoint, relay)
42+
metrics.GetOrCreateHistogram(l).Update(latency)
43+
}
44+
45+
func RecordRelayStatusCode(httpStatus, endpoint, relay string) {
46+
l := fmt.Sprintf(relayStatusCodeLabel, httpStatus, endpoint, relay)
47+
metrics.GetOrCreateCounter(l).Inc()
48+
}
49+
50+
func RecordRelayLastSlot(relay string, slot uint64) {
51+
l := fmt.Sprintf(relayLastSlotLabel, relay)
52+
metrics.GetOrCreateGauge(l, nil).Set(float64(slot))
53+
}
54+
55+
func RecordMsIntoSlot(endpoint string, ms float64) {
56+
l := fmt.Sprintf(msIntoSlotLabel, endpoint)
57+
metrics.GetOrCreateHistogram(l).Update(ms)
58+
}

server/register_validator.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"context"
66
"fmt"
77
"net/http"
8+
"strconv"
9+
"time"
810

911
"github.com/flashbots/mev-boost/server/params"
1012
"github.com/flashbots/mev-boost/server/types"
@@ -45,14 +47,17 @@ func (m *BoostService) registerValidator(log *logrus.Entry, regBytes []byte, hea
4547
}).Debug("sending the registerValidator request")
4648

4749
// Send the request
50+
start := time.Now()
4851
resp, err := m.httpClientRegVal.Do(req)
52+
RecordRelayLatency(params.PathRegisterValidator, relay.String(), float64(time.Since(start).Microseconds()))
4953
if err != nil {
5054
log.WithError(err).Warn("error calling registerValidator on relay")
5155
respErrCh <- err
5256
return
5357
}
5458
resp.Body.Close()
5559

60+
RecordRelayStatusCode(strconv.Itoa(resp.StatusCode), params.PathRegisterValidator, relay.String())
5661
// Check if response is successful
5762
if resp.StatusCode == http.StatusOK {
5863
log.Debug("relay accepted registrations")

0 commit comments

Comments
 (0)