From 9409929607966f1783e2394e5d5def8c39901314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fri=C3=B0rik=20=C3=81smundsson?= Date: Fri, 29 Nov 2024 18:33:26 +0000 Subject: [PATCH] Add telemetry for beacon and eth version (#2176) Co-authored-by: Cal Bera --- beacond/cmd/defaults.go | 4 +- beacond/cmd/types.go | 5 +- mod/execution/pkg/client/client.go | 20 +++ mod/execution/pkg/client/engine.go | 2 +- mod/execution/pkg/client/ethclient/engine.go | 7 +- .../pkg/components/reporting_service.go | 19 ++- .../pkg/components/service_registry.go | 6 +- mod/node-core/pkg/components/types.go | 4 - mod/node-core/pkg/services/version/metrics.go | 73 -------- mod/node-core/pkg/services/version/types.go | 3 + mod/node-core/pkg/services/version/version.go | 161 ++++++++++++++++-- 11 files changed, 207 insertions(+), 97 deletions(-) delete mode 100644 mod/node-core/pkg/services/version/metrics.go diff --git a/beacond/cmd/defaults.go b/beacond/cmd/defaults.go index 43afbaa3d..7bc419670 100644 --- a/beacond/cmd/defaults.go +++ b/beacond/cmd/defaults.go @@ -104,7 +104,9 @@ func DefaultComponents() []any { *BeaconBlockHeader, *BeaconState, *BeaconStateMarshallable, *ExecutionPayload, *ExecutionPayloadHeader, *KVStore, *Logger, ], - components.ProvideReportingService[*Logger], + components.ProvideReportingService[ + *ExecutionPayload, *PayloadAttributes, *Logger, + ], components.ProvideCometBFTService[*Logger], components.ProvideServiceRegistry[ *AvailabilityStore, diff --git a/beacond/cmd/types.go b/beacond/cmd/types.go index 449bd573d..e20e866cf 100644 --- a/beacond/cmd/types.go +++ b/beacond/cmd/types.go @@ -162,7 +162,10 @@ type ( NodeAPIServer = server.Server[NodeAPIContext] // ReportingService is a type alias for the reporting service. - ReportingService = version.ReportingService + ReportingService = version.ReportingService[ + *ExecutionPayload, + *PayloadAttributes, + ] // SidecarFactory is a type alias for the sidecar factory. SidecarFactory = dablob.SidecarFactory[ diff --git a/mod/execution/pkg/client/client.go b/mod/execution/pkg/client/client.go index 22ef724e9..8a54dd8ba 100644 --- a/mod/execution/pkg/client/client.go +++ b/mod/execution/pkg/client/client.go @@ -24,6 +24,7 @@ import ( "context" "math/big" "strings" + "sync" "time" "github.com/berachain/beacon-kit/mod/errors" @@ -51,6 +52,10 @@ type EngineClient[ metrics *clientMetrics // capabilities is a map of capabilities that the execution client has. capabilities map[string]struct{} + // connected will be set to true when we have successfully connected + // to the execution client. + connectedMu sync.RWMutex + connected bool } // New creates a new engine client EngineClient. @@ -82,6 +87,7 @@ func New[ capabilities: make(map[string]struct{}), eth1ChainID: eth1ChainID, metrics: newClientMetrics(telemetrySink, logger), + connected: false, } } @@ -130,11 +136,25 @@ func (s *EngineClient[ } continue } + s.connectedMu.Lock() + s.connected = true + s.connectedMu.Unlock() return nil } } } +func (s *EngineClient[_, _]) IsConnected() bool { + s.connectedMu.RLock() + defer s.connectedMu.RUnlock() + return s.connected +} + +func (s *EngineClient[_, _]) HasCapability(capability string) bool { + _, ok := s.capabilities[capability] + return ok +} + /* -------------------------------------------------------------------------- */ /* Helpers */ /* -------------------------------------------------------------------------- */ diff --git a/mod/execution/pkg/client/engine.go b/mod/execution/pkg/client/engine.go index 0d2e02ee5..2bcc0f8f5 100644 --- a/mod/execution/pkg/client/engine.go +++ b/mod/execution/pkg/client/engine.go @@ -188,7 +188,7 @@ func (s *EngineClient[ // Log the capabilities that the execution client does not have. for _, capability := range ethclient.BeaconKitSupportedCapabilities() { - if _, exists := s.capabilities[capability]; !exists { + if !s.HasCapability(capability) { s.logger.Warn( "Your execution client may require an update 🚸", "unsupported_capability", capability, diff --git a/mod/execution/pkg/client/ethclient/engine.go b/mod/execution/pkg/client/ethclient/engine.go index 3db74e302..01b5e171f 100644 --- a/mod/execution/pkg/client/ethclient/engine.go +++ b/mod/execution/pkg/client/ethclient/engine.go @@ -27,6 +27,7 @@ import ( "github.com/berachain/beacon-kit/mod/primitives/pkg/common" "github.com/berachain/beacon-kit/mod/primitives/pkg/eip4844" "github.com/berachain/beacon-kit/mod/primitives/pkg/version" + "github.com/ethereum/go-ethereum/beacon/engine" ) /* -------------------------------------------------------------------------- */ @@ -180,8 +181,12 @@ func (s *Client[ExecutionPayloadT]) GetClientVersionV1( ctx context.Context, ) ([]engineprimitives.ClientVersionV1, error) { result := make([]engineprimitives.ClientVersionV1, 0) + + // NOTE: although the ethereum spec does not require us passing a + // clientversion as param, it seems some clients require it and even + // enforce a valid Code. if err := s.Call( - ctx, &result, GetClientVersionV1, nil, + ctx, &result, GetClientVersionV1, engine.ClientVersionV1{Code: "GE"}, ); err != nil { return nil, err } diff --git a/mod/node-core/pkg/components/reporting_service.go b/mod/node-core/pkg/components/reporting_service.go index 94df432f3..3fb034d57 100644 --- a/mod/node-core/pkg/components/reporting_service.go +++ b/mod/node-core/pkg/components/reporting_service.go @@ -22,28 +22,41 @@ package components import ( "cosmossdk.io/depinject" + "github.com/berachain/beacon-kit/mod/execution/pkg/client" "github.com/berachain/beacon-kit/mod/log" "github.com/berachain/beacon-kit/mod/node-core/pkg/components/metrics" "github.com/berachain/beacon-kit/mod/node-core/pkg/services/version" + "github.com/berachain/beacon-kit/mod/primitives/pkg/constraints" sdkversion "github.com/cosmos/cosmos-sdk/version" ) type ReportingServiceInput[ + ExecutionPayloadT constraints.EngineType[ExecutionPayloadT], + PayloadAttributesT client.PayloadAttributes, LoggerT log.AdvancedLogger[LoggerT], ] struct { depinject.In Logger LoggerT TelemetrySink *metrics.TelemetrySink + EngineClient *client.EngineClient[ + ExecutionPayloadT, + PayloadAttributesT, + ] } func ProvideReportingService[ + ExecutionPayloadT constraints.EngineType[ExecutionPayloadT], + PayloadAttributesT client.PayloadAttributes, LoggerT log.AdvancedLogger[LoggerT], ]( - in ReportingServiceInput[LoggerT], -) *ReportingService { - return version.NewReportingService( + in ReportingServiceInput[ExecutionPayloadT, PayloadAttributesT, LoggerT], +) *version.ReportingService[ExecutionPayloadT, PayloadAttributesT] { + return version.NewReportingService[ + ExecutionPayloadT, PayloadAttributesT, + ]( in.Logger.With("service", "reporting"), in.TelemetrySink, sdkversion.Version, + in.EngineClient, ) } diff --git a/mod/node-core/pkg/components/service_registry.go b/mod/node-core/pkg/components/service_registry.go index 332df87ad..7eff545f0 100644 --- a/mod/node-core/pkg/components/service_registry.go +++ b/mod/node-core/pkg/components/service_registry.go @@ -35,6 +35,7 @@ import ( "github.com/berachain/beacon-kit/mod/node-api/server" "github.com/berachain/beacon-kit/mod/node-core/pkg/components/metrics" service "github.com/berachain/beacon-kit/mod/node-core/pkg/services/registry" + "github.com/berachain/beacon-kit/mod/node-core/pkg/services/version" "github.com/berachain/beacon-kit/mod/observability/pkg/telemetry" ) @@ -101,7 +102,10 @@ type ServiceRegistryInput[ ] Logger LoggerT NodeAPIServer *server.Server[NodeAPIContextT] - ReportingService *ReportingService + ReportingService *version.ReportingService[ + ExecutionPayloadT, + *engineprimitives.PayloadAttributes[WithdrawalT], + ] TelemetrySink *metrics.TelemetrySink TelemetryService *telemetry.Service ValidatorService *validator.Service[ diff --git a/mod/node-core/pkg/components/types.go b/mod/node-core/pkg/components/types.go index 2abea7cca..cf3e95053 100644 --- a/mod/node-core/pkg/components/types.go +++ b/mod/node-core/pkg/components/types.go @@ -27,7 +27,6 @@ import ( consruntimetypes "github.com/berachain/beacon-kit/mod/consensus/pkg/types" engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives" "github.com/berachain/beacon-kit/mod/node-core/pkg/components/signer" - "github.com/berachain/beacon-kit/mod/node-core/pkg/services/version" "github.com/berachain/beacon-kit/mod/primitives/pkg/async" "github.com/berachain/beacon-kit/mod/primitives/pkg/transition" "github.com/berachain/beacon-kit/mod/storage/pkg/manager" @@ -40,9 +39,6 @@ import ( type ( // DBManager is a type alias for the database manager. DBManager = manager.DBManager - - // ReportingService is a type alias for the reporting service. - ReportingService = version.ReportingService ) /* -------------------------------------------------------------------------- */ diff --git a/mod/node-core/pkg/services/version/metrics.go b/mod/node-core/pkg/services/version/metrics.go deleted file mode 100644 index c0d64d453..000000000 --- a/mod/node-core/pkg/services/version/metrics.go +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -// -// Copyright (C) 2024, Berachain Foundation. All rights reserved. -// Use of this software is governed by the Business Source License included -// in the LICENSE file of this repository and at www.mariadb.com/bsl11. -// -// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY -// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER -// VERSIONS OF THE LICENSED WORK. -// -// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF -// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF -// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE). -// -// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON -// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, -// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND -// TITLE. - -package version - -import ( - "fmt" - "runtime" - - "github.com/berachain/beacon-kit/mod/log" -) - -// versionMetrics holds metrics related to the version reporting. -type versionMetrics struct { - // system is the current system the node is running on. - system string - // logger is the logger used to log information about the version. - logger log.Logger - // sink is the telemetry sink used to report metrics. - sink TelemetrySink -} - -// newVersionMetrics creates a new instance of versionMetrics. -func newVersionMetrics( - logger log.Logger, - sink TelemetrySink, -) *versionMetrics { - return &versionMetrics{ - system: runtime.GOOS + "/" + runtime.GOARCH, - logger: logger, - sink: sink, - } -} - -// reportVersion increments the versionReported counter. -func (vm *versionMetrics) reportVersion(version string) { - vm.logger.Info(fmt.Sprintf(` - - - +==========================================================================+ - + ⭐️ Star BeaconKit on GitHub @ https://github.com/berachain/beacon-kit + - + 🧩 Your node is running version: %-40s+ - + 💾 Your system: %-57s+ - + 🦺 Please report issues @ https://github.com/berachain/beacon-kit/issues + - +==========================================================================+ - - -`, - version, - vm.system, - )) - vm.sink.IncrementCounter( - "beacon_kit.runtime.version.reported", - "version", version, "system", vm.system, - ) -} diff --git a/mod/node-core/pkg/services/version/types.go b/mod/node-core/pkg/services/version/types.go index 16fb08b0f..9c2365c0b 100644 --- a/mod/node-core/pkg/services/version/types.go +++ b/mod/node-core/pkg/services/version/types.go @@ -25,4 +25,7 @@ type TelemetrySink interface { // IncrementCounter increments a counter metric identified by the provided // keys. IncrementCounter(key string, args ...string) + // SetGauge sets a gauge metric to the specified value, identified by the + // provided keys. + SetGauge(key string, value int64, args ...string) } diff --git a/mod/node-core/pkg/services/version/version.go b/mod/node-core/pkg/services/version/version.go index b204bcc9e..863070d8f 100644 --- a/mod/node-core/pkg/services/version/version.go +++ b/mod/node-core/pkg/services/version/version.go @@ -22,9 +22,16 @@ package version import ( "context" + "fmt" + "runtime" "time" + engineprimitives "github.com/berachain/beacon-kit/mod/engine-primitives/pkg/engine-primitives" + "github.com/berachain/beacon-kit/mod/errors" + "github.com/berachain/beacon-kit/mod/execution/pkg/client" + "github.com/berachain/beacon-kit/mod/execution/pkg/client/ethclient" "github.com/berachain/beacon-kit/mod/log" + "github.com/berachain/beacon-kit/mod/primitives/pkg/constraints" ) // defaultReportingInterval is the default interval at which the version is @@ -33,51 +40,181 @@ const defaultReportingInterval = 5 * time.Minute // ReportingService is a service that periodically logs the running chain // version. -type ReportingService struct { +type ReportingService[ + ExecutionPayloadT constraints.EngineType[ExecutionPayloadT], + PayloadAttributesT client.PayloadAttributes, +] struct { // logger is used to log information about the running chain version. logger log.Logger // version represents the current version of the running chain. version string // reportingInterval is the interval at which the version is reported. reportingInterval time.Duration - // metrics contains the metrics for the version service. - metrics *versionMetrics + // sink is the telemetry sink used to report metrics. + sink TelemetrySink + // client to query the execution layer + client *client.EngineClient[ExecutionPayloadT, PayloadAttributesT] } // NewReportingService creates a new VersionReporterService. -func NewReportingService( +func NewReportingService[ + ExecutionPayloadT constraints.EngineType[ExecutionPayloadT], + PayloadAttributesT client.PayloadAttributes, +]( logger log.Logger, telemetrySink TelemetrySink, version string, -) *ReportingService { - return &ReportingService{ + engineClient *client.EngineClient[ExecutionPayloadT, PayloadAttributesT], +) *ReportingService[ + ExecutionPayloadT, PayloadAttributesT, +] { + return &ReportingService[ + ExecutionPayloadT, PayloadAttributesT, + ]{ logger: logger, version: version, reportingInterval: defaultReportingInterval, - metrics: newVersionMetrics(logger, telemetrySink), + sink: telemetrySink, + client: engineClient, } } // Name returns the name of the service. -func (*ReportingService) Name() string { +func (*ReportingService[_, _]) Name() string { return "reporting" } // Start begins the periodic logging of the chain version. -func (v *ReportingService) Start(ctx context.Context) error { - ticker := time.NewTicker(v.reportingInterval) - v.metrics.reportVersion(v.version) +func (rs *ReportingService[_, _]) Start(ctx context.Context) error { + // we print to console always at the beginning + rs.printToConsole(engineprimitives.ClientVersionV1{ + Version: "unknown", + Name: "unknown"}, + ) + + connectedTicker := time.NewTicker(time.Second) go func() { + // wait until the client is connected + connected := false + for !connected { + select { + case <-connectedTicker.C: + connected = rs.client.IsConnected() + case <-ctx.Done(): + connectedTicker.Stop() + return + } + } + connectedTicker.Stop() + + rs.logger.Info("Connected to execution client") + + // log telemetry immediately after we are connected + ethVersion, err := rs.GetEthVersion(ctx) + if err != nil { + rs.logger.Warn("Failed to get eth version", "err", err) + } + rs.logTelemetry(ethVersion) + + // then we start reporting at the reportingInterval interval + ticker := time.NewTicker(rs.reportingInterval) defer ticker.Stop() for { select { case <-ticker.C: - v.metrics.reportVersion(v.version) + // since the eth client can be updated separately for beacon node + // we need to fetch the version every time + ethVersion, err = rs.GetEthVersion(ctx) + if err != nil { + rs.logger.Warn("Failed to get eth version", "err", err) + } + + // print to console and log telemetry + rs.printToConsole(ethVersion) + rs.logTelemetry(ethVersion) + continue case <-ctx.Done(): return } } }() + return nil } + +func (rs *ReportingService[_, _]) printToConsole( + ethClient engineprimitives.ClientVersionV1) { + rs.logger.Info(fmt.Sprintf(` + + + +==========================================================================+ + + ⭐️ Star BeaconKit on GitHub @ https://github.com/berachain/beacon-kit + + + 🧩 Your node is running version: %-40s+ + + ♦ Eth client: %-59s+ + + 💾 Your system: %-57s+ + + 🦺 Please report issues @ https://github.com/berachain/beacon-kit/issues + + +==========================================================================+ + + +`, + rs.version, + fmt.Sprintf("%s (version: %s)", ethClient.Name, ethClient.Version), + runtime.GOOS+"/"+runtime.GOARCH, + )) +} + +func (rs *ReportingService[_, _]) GetEthVersion( + ctx context.Context) (engineprimitives.ClientVersionV1, error) { + ethVersion := engineprimitives.ClientVersionV1{ + Version: "unknown", + Name: "unknown", + } + + if rs.client.HasCapability(ethclient.GetClientVersionV1) { + // Get the client version from the execution layer. + info, err := rs.client.GetClientVersionV1(ctx) + if err != nil { + return ethVersion, fmt.Errorf("failed to get client version: %w", err) + } + + // the spec says we should have at least one client version + if len(info) == 0 { + return ethVersion, errors.New("no client version returned") + } + + ethVersion.Version = info[0].Version + ethVersion.Name = info[0].Name + } else { + rs.logger.Warn("Client does not have capability to get client version") + } + + return ethVersion, nil +} + +func (rs *ReportingService[_, _]) logTelemetry( + ethVersion engineprimitives.ClientVersionV1) { + systemInfo := runtime.GOOS + "/" + runtime.GOARCH + + // TODO: Delete this counter as it should be included in the new + // beacon_kit.runtime.version metric. + rs.sink.IncrementCounter( + "beacon_kit.runtime.version.reported", + "version", rs.version, "system", systemInfo, + ) + + rs.logger.Info("Reporting version", "version", rs.version, + "system", systemInfo, + "eth_version", ethVersion.Version, + "eth_name", ethVersion.Name) + + // Report the version to the telemetry sink and include labels + // for beacon node version and eth name and version + var args = [8]string{ + "version", rs.version, + "system", systemInfo, + "eth_version", ethVersion.Version, + "eth_name", ethVersion.Name, + } + rs.sink.SetGauge("beacon_kit.runtime.version", 1, args[:]...) +}