Skip to content

Commit

Permalink
Merge pull request #9 from ethpandaops/feat/account-check-interval
Browse files Browse the repository at this point in the history
feat: eoa -> account naming with check interval config
  • Loading branch information
Savid authored Nov 6, 2022
2 parents 96bfe10 + 451db42 commit 7b9ea99
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 97 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A Prometheus metrics exporter for Ethereum externally owned account and contract addresses including;

- Externally owned account addresses
- [Externally owned account and contract](https://ethereum.org/en/developers/docs/accounts) addresses
- [ERC20](https://eips.ethereum.org/EIPS/eip-20) contracts
- [ERC721](https://eips.ethereum.org/EIPS/eip-20) contracts
- [ERC1155](https://eips.ethereum.org/EIPS/eip-20) contracts
Expand Down Expand Up @@ -32,14 +32,15 @@ Ethereum Address Metrics Exporter relies entirely on a single `yaml` config file
| global.logging | `warn` | Log level (`panic`, `fatal`, `warn`, `info`, `debug`, `trace`) |
| global.metricsAddr | `:9090` | The address the metrics server will listen on |
| global.namespace | `eth_address` | The prefix added to every metric |
| global.checkInterval | `15s` | How often the service should check the addresses for balance |
| global.labels[] | | Key value pair of labels to add to every metric (optional) |
| execution.url | `http://localhost:8545` | URL to the execution node |
| execution.timeout | `10s` | Timeout for requests to the execution node |
| execution.headers[] | | Key value pair of headers to add on every request |
| addresses.eoa | | List of ethereum externally owned account addresses |
| addresses.eoa[].name | | Name of the address, will be a label on the metric |
| addresses.eoa[].address | | Ethereum externally owned account address |
| addresses.eoa[].labels[] | | Key value pair of labels to add to this address only (optional) |
| addresses.account | | List of ethereum externally owned account or contract addresses |
| addresses.account[].name | | Name of the address, will be a label on the metric |
| addresses.account[].address | | Account address |
| addresses.account[].labels[] | | Key value pair of labels to add to this address only (optional) |
| addresses.erc20 | | List of ethereum [ERC20](https://eips.ethereum.org/EIPS/eip-20) addresses |
| addresses.erc20[].name | | Name of the address, will be a label on the metric |
| addresses.erc20[].address | | Ethereum address |
Expand Down Expand Up @@ -87,7 +88,7 @@ execution:
authorization: "Basic abc123"

addresses:
eoa:
account:
- name: John smith
address: 0x4B1D3c9BEf9D097F564DcD6cdF4558CB389bE3d5
labels:
Expand Down
17 changes: 9 additions & 8 deletions pkg/exporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ type Config struct {
}

type GlobalConfig struct {
LoggingLevel string `yaml:"logging" default:"warn"`
MetricsAddr string `yaml:"metricsAddr" default:":9090"`
Namespace string `yaml:"namespace" default:"eth_address"`
Labels map[string]string `yaml:"labels"`
LoggingLevel string `yaml:"logging" default:"warn"`
MetricsAddr string `yaml:"metricsAddr" default:":9090"`
Namespace string `yaml:"namespace" default:"eth_address"`
CheckInterval time.Duration `yaml:"checkInterval" default:"15s"`
Labels map[string]string `yaml:"labels"`
}

// ExecutionNode represents a single ethereum execution client.
Expand All @@ -31,7 +32,7 @@ type ExecutionNode struct {
}

type Addresses struct {
EOA []*jobs.AddressEOA `yaml:"eoa"`
Account []*jobs.AddressAccount `yaml:"account"`
ERC20 []*jobs.AddressERC20 `yaml:"erc20"`
ERC721 []*jobs.AddressERC721 `yaml:"erc721"`
ERC1155 []*jobs.AddressERC1155 `yaml:"erc1155"`
Expand All @@ -40,12 +41,12 @@ type Addresses struct {
}

func (c *Config) Validate() error {
// Check that all addresses have different names
// Check that all account addresses have different names
duplicates := make(map[string]struct{})
for _, u := range c.Addresses.EOA {
for _, u := range c.Addresses.Account {
// Check that all addresses have different names
if _, ok := duplicates[u.Name]; ok {
return fmt.Errorf("there's a duplicate eoa addresses with the same name: %s", u.Name)
return fmt.Errorf("there's a duplicate account addresses with the same name: %s", u.Name)
}

duplicates[u.Name] = struct{}{}
Expand Down
2 changes: 1 addition & 1 deletion pkg/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (e *exporter) Start(ctx context.Context) error {

e.execution = api.NewExecutionClient(e.log, e.Cfg.GlobalConfig.Namespace, e.Cfg.Execution.URL, e.Cfg.Execution.Headers, e.Cfg.Execution.Timeout)

e.metrics = NewMetrics(e.execution, e.log, e.Cfg.GlobalConfig.Namespace, e.Cfg.GlobalConfig.Labels, &e.Cfg.Addresses)
e.metrics = NewMetrics(e.execution, e.log, e.Cfg.GlobalConfig.CheckInterval, e.Cfg.GlobalConfig.Namespace, e.Cfg.GlobalConfig.Labels, &e.Cfg.Addresses)

e.log.
WithField("execution_url", e.Cfg.Execution.URL).
Expand Down
71 changes: 37 additions & 34 deletions pkg/exporter/jobs/eoa.go → pkg/exporter/jobs/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,34 @@ import (
"github.com/sirupsen/logrus"
)

// Eth exposes metrics for ethereum externally owned account addresses
type EOA struct {
client api.ExecutionClient
log logrus.FieldLogger
EOABalance prometheus.GaugeVec
EOAError prometheus.CounterVec
addresses []*AddressEOA
labelsMap map[string]int
// Eth exposes metrics for account addresses
type Account struct {
client api.ExecutionClient
log logrus.FieldLogger
AccountBalance prometheus.GaugeVec
AccountError prometheus.CounterVec
checkInterval time.Duration
addresses []*AddressAccount
labelsMap map[string]int
}

type AddressEOA struct {
type AddressAccount struct {
Address string `yaml:"address"`
Name string `yaml:"name"`
Labels map[string]string `yaml:"labels"`
}

const (
NameEOA = "eoa"
NameAccount = "account"
)

func (n *EOA) Name() string {
return NameEOA
func (n *Account) Name() string {
return NameAccount
}

// NewEOA returns a new EOA instance.
func NewEOA(client api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressEOA) EOA {
namespace += "_" + NameEOA
// NewAccount returns a new Account instance.
func NewAccount(client api.ExecutionClient, log logrus.FieldLogger, checkInterval time.Duration, namespace string, constLabels map[string]string, addresses []*AddressAccount) Account {
namespace += "_" + NameAccount

labelsMap := map[string]int{}
labelsMap[LabelName] = 0
Expand All @@ -54,62 +55,64 @@ func NewEOA(client api.ExecutionClient, log logrus.FieldLogger, namespace string
labels[index] = label
}

instance := EOA{
client: client,
log: log.WithField("module", NameEOA),
addresses: addresses,
labelsMap: labelsMap,
EOABalance: *prometheus.NewGaugeVec(
instance := Account{
client: client,
log: log.WithField("module", NameAccount),
addresses: addresses,
checkInterval: checkInterval,
labelsMap: labelsMap,
AccountBalance: *prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Name: "balance",
Help: "The balance of a ethereum externally owned account address.",
Help: "The balance of a account address.",
ConstLabels: constLabels,
},
labels,
),
EOAError: *prometheus.NewCounterVec(
AccountError: *prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Name: "errors_total",
Help: "The total errors when getting the balance of a ethereum externally owned account address.",
Help: "The total errors when getting the balance of a account address.",
ConstLabels: constLabels,
},
labels,
),
}

prometheus.MustRegister(instance.EOABalance)
prometheus.MustRegister(instance.EOAError)
prometheus.MustRegister(instance.AccountBalance)
prometheus.MustRegister(instance.AccountError)

return instance
}

func (n *EOA) Start(ctx context.Context) {
func (n *Account) Start(ctx context.Context) {
n.tick(ctx)

for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 15):
case <-time.After(n.checkInterval):
n.log.WithField("asd", n.checkInterval).Debug("Tick")
n.tick(ctx)
}
}
}

//nolint:unparam // context will be used in the future
func (n *EOA) tick(ctx context.Context) {
func (n *Account) tick(ctx context.Context) {
for _, address := range n.addresses {
err := n.getBalance(address)

if err != nil {
n.log.WithError(err).WithField("address", address).Error("Failed to get EOA balance")
n.log.WithError(err).WithField("address", address).Error("Failed to get Account balance")
}
}
}

func (n *EOA) getLabelValues(address *AddressEOA) []string {
func (n *Account) getLabelValues(address *AddressAccount) []string {
values := make([]string, len(n.labelsMap))

for label, index := range n.labelsMap {
Expand All @@ -130,12 +133,12 @@ func (n *EOA) getLabelValues(address *AddressEOA) []string {
return values
}

func (n *EOA) getBalance(address *AddressEOA) error {
func (n *Account) getBalance(address *AddressAccount) error {
var err error

defer func() {
if err != nil {
n.EOAError.WithLabelValues(n.getLabelValues(address)...).Inc()
n.AccountError.WithLabelValues(n.getLabelValues(address)...).Inc()
}
}()

Expand All @@ -145,7 +148,7 @@ func (n *EOA) getBalance(address *AddressEOA) error {
}

balanceFloat64 := hexStringToFloat64(balance)
n.EOABalance.WithLabelValues(n.getLabelValues(address)...).Set(balanceFloat64)
n.AccountBalance.WithLabelValues(n.getLabelValues(address)...).Set(balanceFloat64)

return nil
}
14 changes: 8 additions & 6 deletions pkg/exporter/jobs/chainlink_data_feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type ChainlinkDataFeed struct {
log logrus.FieldLogger
ChainlinkDataFeedBalance prometheus.GaugeVec
ChainlinkDataFeedError prometheus.CounterVec
checkInterval time.Duration
addresses []*AddressChainlinkDataFeed
labelsMap map[string]int
}
Expand All @@ -36,7 +37,7 @@ func (n *ChainlinkDataFeed) Name() string {
}

// NewChainlinkDataFeed returns a new ChainlinkDataFeed instance.
func NewChainlinkDataFeed(client api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressChainlinkDataFeed) ChainlinkDataFeed {
func NewChainlinkDataFeed(client api.ExecutionClient, log logrus.FieldLogger, checkInterval time.Duration, namespace string, constLabels map[string]string, addresses []*AddressChainlinkDataFeed) ChainlinkDataFeed {
namespace += "_" + NameChainlinkDataFeed

labelsMap := map[string]int{
Expand All @@ -60,10 +61,11 @@ func NewChainlinkDataFeed(client api.ExecutionClient, log logrus.FieldLogger, na
}

instance := ChainlinkDataFeed{
client: client,
log: log.WithField("module", NameChainlinkDataFeed),
addresses: addresses,
labelsMap: labelsMap,
client: client,
log: log.WithField("module", NameChainlinkDataFeed),
addresses: addresses,
checkInterval: checkInterval,
labelsMap: labelsMap,
ChainlinkDataFeedBalance: *prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Expand Down Expand Up @@ -97,7 +99,7 @@ func (n *ChainlinkDataFeed) Start(ctx context.Context) {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 15):
case <-time.After(n.checkInterval):
n.tick(ctx)
}
}
Expand Down
14 changes: 8 additions & 6 deletions pkg/exporter/jobs/erc1155.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type ERC1155 struct {
log logrus.FieldLogger
ERC1155Balance prometheus.GaugeVec
ERC1155Error prometheus.CounterVec
checkInterval time.Duration
addresses []*AddressERC1155
labelsMap map[string]int
}
Expand All @@ -38,7 +39,7 @@ func (n *ERC1155) Name() string {
}

// NewERC1155 returns a new ERC1155 instance.
func NewERC1155(client api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressERC1155) ERC1155 {
func NewERC1155(client api.ExecutionClient, log logrus.FieldLogger, checkInterval time.Duration, namespace string, constLabels map[string]string, addresses []*AddressERC1155) ERC1155 {
namespace += "_" + NameERC1155

labelsMap := map[string]int{
Expand All @@ -62,10 +63,11 @@ func NewERC1155(client api.ExecutionClient, log logrus.FieldLogger, namespace st
}

instance := ERC1155{
client: client,
log: log.WithField("module", NameERC1155),
addresses: addresses,
labelsMap: labelsMap,
client: client,
log: log.WithField("module", NameERC1155),
addresses: addresses,
checkInterval: checkInterval,
labelsMap: labelsMap,
ERC1155Balance: *prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Expand Down Expand Up @@ -99,7 +101,7 @@ func (n *ERC1155) Start(ctx context.Context) {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 15):
case <-time.After(n.checkInterval):
n.tick(ctx)
}
}
Expand Down
26 changes: 14 additions & 12 deletions pkg/exporter/jobs/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import (

// ERC20 exposes metrics for ethereum ERC20 contract by address
type ERC20 struct {
client api.ExecutionClient
log logrus.FieldLogger
ERC20Balance prometheus.GaugeVec
ERC20Error prometheus.CounterVec
addresses []*AddressERC20
labelsMap map[string]int
client api.ExecutionClient
log logrus.FieldLogger
ERC20Balance prometheus.GaugeVec
ERC20Error prometheus.CounterVec
checkInterval time.Duration
addresses []*AddressERC20
labelsMap map[string]int
}

type AddressERC20 struct {
Expand All @@ -35,7 +36,7 @@ func (n *ERC20) Name() string {
}

// NewERC20 returns a new ERC20 instance.
func NewERC20(client api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressERC20) ERC20 {
func NewERC20(client api.ExecutionClient, log logrus.FieldLogger, checkInterval time.Duration, namespace string, constLabels map[string]string, addresses []*AddressERC20) ERC20 {
namespace += "_" + NameERC20

labelsMap := map[string]int{
Expand All @@ -59,10 +60,11 @@ func NewERC20(client api.ExecutionClient, log logrus.FieldLogger, namespace stri
}

instance := ERC20{
client: client,
log: log.WithField("module", NameERC20),
addresses: addresses,
labelsMap: labelsMap,
client: client,
log: log.WithField("module", NameERC20),
addresses: addresses,
checkInterval: checkInterval,
labelsMap: labelsMap,
ERC20Balance: *prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Expand Down Expand Up @@ -96,7 +98,7 @@ func (n *ERC20) Start(ctx context.Context) {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 15):
case <-time.After(n.checkInterval):
n.tick(ctx)
}
}
Expand Down
Loading

0 comments on commit 7b9ea99

Please sign in to comment.