Skip to content

Commit

Permalink
feat(x/metrics): add module for emiting custom chain metrics (backport
Browse files Browse the repository at this point in the history
…#1668) (#1676)

* feat(x/metrics): add module for emiting custom chain metrics (#1668)

* initialize x/metrics with metrics collection

* include global labels in x/metrics metrics

* add x/metrics spec

* add x/metrics test coverage

* update changelog

(cherry picked from commit 9a0aed7)

# Conflicts:
#	CHANGELOG.md

* fix changelog merge conflict

---------

Co-authored-by: Robert Pirtle <Astropirtle@gmail.com>
  • Loading branch information
mergify[bot] and pirtleshell committed Aug 25, 2023
1 parent 75857cd commit bf28435
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 2 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### Features

- (metrics) [#1668] Adds non-state breaking x/metrics module for custom telemetry.

## [v0.23.2]

### Bug Fixes

- (deps) [#1622] Bump tm-db to v0.6.7-kava.3 to return rocksdb open error

## [v0.23.1](https://github.com/Kava-Labs/kava/releases/tag/v0.23.1)

This upgrade follows https://forum.cosmos.network/t/ibc-security-advisory-huckleberry/10731 and updates IBC from v6.1.0 to v6.1.1.

## [v0.23.0]

### Improvements

- (deps) [#1477] Bump Cosmos SDK to v0.46.10.
Expand Down Expand Up @@ -238,6 +254,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
large-scale simulations remotely using aws-batch

[#1668]: https://github.com/Kava-Labs/kava/pull/1668
[#1631]: https://github.com/Kava-Labs/kava/pull/1631
[#1622]: https://github.com/Kava-Labs/kava/pull/1622
[#1568]: https://github.com/Kava-Labs/kava/pull/1568
Expand Down
8 changes: 8 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ import (
"github.com/kava-labs/kava/x/liquid"
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
metrics "github.com/kava-labs/kava/x/metrics"
metricstypes "github.com/kava-labs/kava/x/metrics/types"
pricefeed "github.com/kava-labs/kava/x/pricefeed"
pricefeedkeeper "github.com/kava-labs/kava/x/pricefeed/keeper"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
Expand Down Expand Up @@ -216,6 +218,7 @@ var (
router.AppModuleBasic{},
mint.AppModuleBasic{},
community.AppModuleBasic{},
metrics.AppModuleBasic{},
)

// module account permissions
Expand Down Expand Up @@ -261,6 +264,7 @@ type Options struct {
MempoolAuthAddresses []sdk.AccAddress
EVMTrace string
EVMMaxGasWanted uint64
TelemetryOptions metricstypes.TelemetryOptions
}

// DefaultOptions is a sensible default Options value.
Expand Down Expand Up @@ -791,10 +795,12 @@ func NewApp(
// nil InflationCalculationFn, use SDK's default inflation function
mint.NewAppModule(appCodec, app.mintKeeper, app.accountKeeper, nil),
community.NewAppModule(app.communityKeeper, app.accountKeeper),
metrics.NewAppModule(options.TelemetryOptions),
)

// Warning: Some begin blockers must run before others. Ensure the dependencies are understood before modifying this list.
app.mm.SetOrderBeginBlockers(
metricstypes.ModuleName,
// Upgrade begin blocker runs migrations on the first block after an upgrade. It should run before any other module.
upgradetypes.ModuleName,
// Capability begin blocker runs non state changing initialization.
Expand Down Expand Up @@ -883,6 +889,7 @@ func NewApp(
routertypes.ModuleName,
minttypes.ModuleName,
communitytypes.ModuleName,
metricstypes.ModuleName,
)

// Warning: Some init genesis methods must run before others. Ensure the dependencies are understood before modifying this list
Expand Down Expand Up @@ -924,6 +931,7 @@ func NewApp(
validatorvestingtypes.ModuleName,
liquidtypes.ModuleName,
routertypes.ModuleName,
metricstypes.ModuleName,
)

app.mm.RegisterInvariants(&app.crisisKeeper)
Expand Down
2 changes: 2 additions & 0 deletions cmd/kava/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/app/params"
metricstypes "github.com/kava-labs/kava/x/metrics/types"
)

const (
Expand Down Expand Up @@ -99,6 +100,7 @@ func (ac appCreator) newApp(
MempoolAuthAddresses: mempoolAuthAddresses,
EVMTrace: cast.ToString(appOpts.Get(ethermintflags.EVMTracer)),
EVMMaxGasWanted: cast.ToUint64(appOpts.Get(ethermintflags.EVMMaxTxGasWanted)),
TelemetryOptions: metricstypes.TelemetryOptionsFromAppOpts(appOpts),
},
baseapp.SetPruning(pruningOpts),
baseapp.SetMinGasPrices(strings.Replace(cast.ToString(appOpts.Get(server.FlagMinGasPrices)), ";", ",", -1)),
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ require (
github.com/cosmos/ibc-go/v6 v6.1.1
github.com/ethereum/go-ethereum v1.10.26
github.com/evmos/ethermint v0.21.0
github.com/go-kit/kit v0.12.0
github.com/gogo/protobuf v1.3.3
github.com/golang/protobuf v1.5.3
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/pelletier/go-toml/v2 v2.0.6
github.com/prometheus/client_golang v1.14.0
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.15.0
Expand Down Expand Up @@ -84,7 +86,6 @@ require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
Expand Down Expand Up @@ -148,7 +149,6 @@ require (
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.40.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
Expand Down
12 changes: 12 additions & 0 deletions x/metrics/abci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package metrics

import (
"github.com/kava-labs/kava/x/metrics/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// BeginBlocker publishes metrics at the start of each block.
func BeginBlocker(ctx sdk.Context, metrics *types.Metrics) {
metrics.LatestBlockHeight.Set(float64(ctx.BlockHeight()))
}
45 changes: 45 additions & 0 deletions x/metrics/abci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package metrics_test

import (
"testing"

kitmetrics "github.com/go-kit/kit/metrics"
"github.com/stretchr/testify/require"

sdk "github.com/cosmos/cosmos-sdk/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/metrics"
"github.com/kava-labs/kava/x/metrics/types"
)

type MockGauge struct {
value float64
}

func (mg *MockGauge) With(labelValues ...string) kitmetrics.Gauge { return mg }
func (mg *MockGauge) Set(value float64) { mg.value = value }
func (*MockGauge) Add(_ float64) {}

func ctxWithHeight(height int64) sdk.Context {
tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates()
return tApp.NewContext(false, tmproto.Header{Height: height})
}

func TestBeginBlockEmitsLatestHeight(t *testing.T) {
gauge := MockGauge{}
myMetrics := &types.Metrics{
LatestBlockHeight: &gauge,
}

metrics.BeginBlocker(ctxWithHeight(1), myMetrics)
require.EqualValues(t, 1, gauge.value)

metrics.BeginBlocker(ctxWithHeight(100), myMetrics)
require.EqualValues(t, 100, gauge.value)

metrics.BeginBlocker(ctxWithHeight(17e6), myMetrics)
require.EqualValues(t, 17e6, gauge.value)
}
125 changes: 125 additions & 0 deletions x/metrics/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package metrics

import (
"encoding/json"

"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"

abci "github.com/tendermint/tendermint/abci/types"

"github.com/kava-labs/kava/x/metrics/types"
)

var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)

// AppModuleBasic app module basics object
type AppModuleBasic struct{}

// Name returns the module name
func (AppModuleBasic) Name() string {
return types.ModuleName
}

// RegisterLegacyAminoCodec register module codec
// Deprecated: unused but necessary to fulfill AppModuleBasic interface
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {}

// DefaultGenesis default genesis state
func (AppModuleBasic) DefaultGenesis(_ codec.JSONCodec) json.RawMessage {
return []byte("{}")
}

// ValidateGenesis module validate genesis
func (AppModuleBasic) ValidateGenesis(_ codec.JSONCodec, _ client.TxEncodingConfig, _ json.RawMessage) error {
return nil
}

// RegisterInterfaces implements InterfaceModule.RegisterInterfaces
func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {}

// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.
func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {}

// GetTxCmd returns the root tx command for the module.
func (AppModuleBasic) GetTxCmd() *cobra.Command {
return nil
}

// GetQueryCmd returns no root query command for the module.
func (AppModuleBasic) GetQueryCmd() *cobra.Command {
return nil
}

//____________________________________________________________________________

// AppModule app module type
type AppModule struct {
AppModuleBasic
metrics *types.Metrics
}

// NewAppModule creates a new AppModule object
func NewAppModule(telemetryOpts types.TelemetryOptions) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
metrics: types.NewMetrics(telemetryOpts),
}
}

// Name module name
func (am AppModule) Name() string {
return am.AppModuleBasic.Name()
}

// RegisterInvariants register module invariants
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}

// Route module message route name
// Deprecated: unused but necessary to fulfill AppModule interface
func (am AppModule) Route() sdk.Route { return sdk.Route{} }

// QuerierRoute module querier route name
// Deprecated: unused but necessary to fulfill AppModule interface
func (AppModule) QuerierRoute() string { return types.ModuleName }

// LegacyQuerierHandler returns no sdk.Querier.
// Deprecated: unused but necessary to fulfill AppModule interface
func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
return nil
}

// ConsensusVersion implements AppModule/ConsensusVersion.
func (AppModule) ConsensusVersion() uint64 { return 1 }

// RegisterServices registers module services.
func (am AppModule) RegisterServices(cfg module.Configurator) {}

// InitGenesis module init-genesis
func (am AppModule) InitGenesis(ctx sdk.Context, _ codec.JSONCodec, _ json.RawMessage) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

// ExportGenesis module export genesis
func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONCodec) json.RawMessage {
return nil
}

// BeginBlock module begin-block
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
BeginBlocker(ctx, am.metrics)
}

// EndBlock module end-block
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}
36 changes: 36 additions & 0 deletions x/metrics/spec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!--
order: 0
title: "Metrics Overview"
parent:
title: "metrics"
-->

# `metrics`


## Abstract

`x/metrics` is a stateless module that does not affect consensus. It captures chain metrics and emits them when the `instrumentation.prometheus` option is enabled in `config.toml`.

## Precision

The metrics emitted by `x/metrics` are `float64`s. They use `github.com/go-kit/kit/metrics` Prometheus gauges. Cosmos-sdk's `telemetry` package was not used because, at the time of writing, it only supports `float32`s and so does not maintain accurate representations of ints larger than ~16.8M. With `float64`s, integers may be accurately represented up to ~9e15.

## Metrics

The following metrics are defined:
* `cometbft_blocksync_latest_block_height` - this emulates the blocksync `latest_block_height` metric in CometBFT v0.38+. The `cometbft` namespace comes from the `instrumentation.namespace` config.toml value.

## Metric Labels

All metrics emitted have the labels defined in app.toml's `telemetry.global-labels` field. This is the same field used by cosmos-sdk's `telemetry` package.

example:
```toml
# app.toml
[telemetry]
global-labels = [
["chain_id", "kava_2222-10"],
["my_label", "my_value"],
]
```
6 changes: 6 additions & 0 deletions x/metrics/types/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package types

const (
// Name of the module
ModuleName = "metrics"
)
Loading

0 comments on commit bf28435

Please sign in to comment.