solana_exporter/cmd/solana_exporter/exporter.go

231 lines
6.6 KiB
Go
Raw Normal View History

package main
import (
2021-01-03 03:23:18 -08:00
"context"
2021-01-03 14:58:24 -08:00
"flag"
2024-10-01 12:38:28 -07:00
"github.com/asymmetric-research/solana_exporter/pkg/rpc"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
2024-10-01 12:38:28 -07:00
"strings"
"time"
2021-01-03 14:58:24 -08:00
"k8s.io/klog/v2"
)
var (
2024-10-01 12:38:28 -07:00
httpTimeout = 60 * time.Second
rpcAddr = flag.String("rpcURI", "", "Solana RPC URI (including protocol and path)")
addr = flag.String("addr", ":8080", "Listen address")
votePubkey = flag.String("votepubkey", "", "Validator vote address (will only return results of this address)")
httpTimeoutSecs = flag.Int("http_timeout", 60, "HTTP timeout in seconds")
balanceAddresses = flag.String("balance-addresses", "", "Comma-separated list of addresses to monitor balances")
)
func init() {
2021-01-03 14:58:24 -08:00
klog.InitFlags(nil)
}
type solanaCollector struct {
2024-10-01 12:38:28 -07:00
rpcClient rpc.Provider
slotPace time.Duration
balanceAddresses []string
totalValidatorsDesc *prometheus.Desc
validatorActivatedStake *prometheus.Desc
validatorLastVote *prometheus.Desc
validatorRootSlot *prometheus.Desc
validatorDelinquent *prometheus.Desc
2022-08-10 10:13:08 -07:00
solanaVersion *prometheus.Desc
2024-10-01 12:38:28 -07:00
balances *prometheus.Desc
}
2024-10-01 12:38:28 -07:00
func createSolanaCollector(provider rpc.Provider, slotPace time.Duration, balanceAddresses []string) *solanaCollector {
return &solanaCollector{
2024-10-01 12:38:28 -07:00
rpcClient: provider,
slotPace: slotPace,
balanceAddresses: balanceAddresses,
totalValidatorsDesc: prometheus.NewDesc(
"solana_active_validators",
"Total number of active validators by state",
2024-10-01 02:52:02 -07:00
[]string{"state"},
nil,
),
validatorActivatedStake: prometheus.NewDesc(
"solana_validator_activated_stake",
"Activated stake per validator",
2024-10-01 02:52:02 -07:00
[]string{"pubkey", "nodekey"},
nil,
),
validatorLastVote: prometheus.NewDesc(
"solana_validator_last_vote",
"Last voted slot per validator",
2024-10-01 02:52:02 -07:00
[]string{"pubkey", "nodekey"},
nil,
),
validatorRootSlot: prometheus.NewDesc(
"solana_validator_root_slot",
"Root slot per validator",
2024-10-01 02:52:02 -07:00
[]string{"pubkey", "nodekey"},
nil,
),
validatorDelinquent: prometheus.NewDesc(
"solana_validator_delinquent",
"Whether a validator is delinquent",
2024-10-01 02:52:02 -07:00
[]string{"pubkey", "nodekey"},
nil,
),
2022-08-10 10:13:08 -07:00
solanaVersion: prometheus.NewDesc(
"solana_node_version",
"Node version of solana",
2024-10-01 02:52:02 -07:00
[]string{"version"},
nil,
),
2024-10-01 12:38:28 -07:00
balances: prometheus.NewDesc(
"solana_account_balance",
"Solana account balances",
[]string{"address"},
nil,
),
}
}
2024-10-01 12:38:28 -07:00
func NewSolanaCollector(rpcAddr string, balanceAddresses []string) *solanaCollector {
return createSolanaCollector(rpc.NewRPCClient(rpcAddr), slotPacerSchedule, balanceAddresses)
2024-06-11 13:23:10 -07:00
}
2021-01-03 07:48:43 -08:00
func (c *solanaCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.totalValidatorsDesc
2022-08-10 10:13:08 -07:00
ch <- c.solanaVersion
2024-06-12 02:21:16 -07:00
ch <- c.validatorActivatedStake
ch <- c.validatorLastVote
ch <- c.validatorRootSlot
ch <- c.validatorDelinquent
2024-10-01 12:38:28 -07:00
ch <- c.balances
}
2024-10-01 12:38:28 -07:00
func (c *solanaCollector) collectVoteAccounts(ctx context.Context, ch chan<- prometheus.Metric) {
params := map[string]string{"commitment": string(rpc.CommitmentRecent)}
if *votePubkey != "" {
params = map[string]string{"commitment": string(rpc.CommitmentRecent), "votePubkey": *votePubkey}
}
voteAccounts, err := c.rpcClient.GetVoteAccounts(ctx, []interface{}{params})
if err != nil {
ch <- prometheus.NewInvalidMetric(c.totalValidatorsDesc, err)
ch <- prometheus.NewInvalidMetric(c.validatorActivatedStake, err)
ch <- prometheus.NewInvalidMetric(c.validatorLastVote, err)
ch <- prometheus.NewInvalidMetric(c.validatorRootSlot, err)
ch <- prometheus.NewInvalidMetric(c.validatorDelinquent, err)
return
}
2024-10-01 02:52:02 -07:00
ch <- prometheus.MustNewConstMetric(
2024-10-01 12:38:28 -07:00
c.totalValidatorsDesc, prometheus.GaugeValue, float64(len(voteAccounts.Delinquent)), "delinquent",
2024-10-01 02:52:02 -07:00
)
ch <- prometheus.MustNewConstMetric(
2024-10-01 12:38:28 -07:00
c.totalValidatorsDesc, prometheus.GaugeValue, float64(len(voteAccounts.Current)), "current",
2024-10-01 02:52:02 -07:00
)
2024-10-01 12:38:28 -07:00
for _, account := range append(voteAccounts.Current, voteAccounts.Delinquent...) {
2024-10-01 02:52:02 -07:00
ch <- prometheus.MustNewConstMetric(
c.validatorActivatedStake,
prometheus.GaugeValue,
float64(account.ActivatedStake),
account.VotePubkey,
account.NodePubkey,
)
ch <- prometheus.MustNewConstMetric(
c.validatorLastVote,
prometheus.GaugeValue,
float64(account.LastVote),
account.VotePubkey,
account.NodePubkey,
)
ch <- prometheus.MustNewConstMetric(
c.validatorRootSlot,
prometheus.GaugeValue,
float64(account.RootSlot),
account.VotePubkey,
account.NodePubkey,
)
}
2024-10-01 12:38:28 -07:00
for _, account := range voteAccounts.Current {
2024-10-01 02:52:02 -07:00
ch <- prometheus.MustNewConstMetric(
c.validatorDelinquent, prometheus.GaugeValue, 0, account.VotePubkey, account.NodePubkey,
)
}
2024-10-01 12:38:28 -07:00
for _, account := range voteAccounts.Delinquent {
2024-10-01 02:52:02 -07:00
ch <- prometheus.MustNewConstMetric(
c.validatorDelinquent, prometheus.GaugeValue, 1, account.VotePubkey, account.NodePubkey,
)
}
}
2024-10-01 12:38:28 -07:00
func (c *solanaCollector) collectVersion(ctx context.Context, ch chan<- prometheus.Metric) {
version, err := c.rpcClient.GetVersion(ctx)
2022-07-29 05:43:52 -07:00
if err != nil {
2024-10-01 12:38:28 -07:00
ch <- prometheus.NewInvalidMetric(c.solanaVersion, err)
return
}
2022-08-10 10:13:08 -07:00
2024-10-01 12:38:28 -07:00
ch <- prometheus.MustNewConstMetric(c.solanaVersion, prometheus.GaugeValue, 1, version)
}
2022-08-10 10:13:08 -07:00
2024-10-01 12:38:28 -07:00
func (c *solanaCollector) collectBalances(ctx context.Context, ch chan<- prometheus.Metric) {
balances, err := fetchBalances(ctx, c.rpcClient, c.balanceAddresses)
2022-08-10 10:13:08 -07:00
if err != nil {
ch <- prometheus.NewInvalidMetric(c.solanaVersion, err)
2024-10-01 12:38:28 -07:00
return
}
for address, balance := range balances {
ch <- prometheus.MustNewConstMetric(c.balances, prometheus.GaugeValue, balance, address)
}
}
func fetchBalances(ctx context.Context, client rpc.Provider, addresses []string) (map[string]float64, error) {
balances := make(map[string]float64)
for _, address := range addresses {
balance, err := client.GetBalance(ctx, address)
if err != nil {
return nil, err
}
balances[address] = balance
2022-08-10 10:13:08 -07:00
}
2024-10-01 12:38:28 -07:00
return balances, nil
}
func (c *solanaCollector) Collect(ch chan<- prometheus.Metric) {
ctx, cancel := context.WithTimeout(context.Background(), httpTimeout)
defer cancel()
c.collectVoteAccounts(ctx, ch)
}
func main() {
2021-01-03 14:58:24 -08:00
flag.Parse()
if *rpcAddr == "" {
klog.Fatal("Please specify -rpcURI")
}
2024-06-04 03:24:54 -07:00
httpTimeout = time.Duration(*httpTimeoutSecs) * time.Second
2024-10-01 12:38:28 -07:00
var monitoredAddresses []string
if *balanceAddresses != "" {
monitoredAddresses = strings.Split(*balanceAddresses, ",")
}
collector := NewSolanaCollector(*rpcAddr, monitoredAddresses)
2021-01-03 14:58:24 -08:00
2024-06-13 15:47:47 -07:00
go collector.WatchSlots(context.Background())
2021-01-03 14:58:24 -08:00
prometheus.MustRegister(collector)
http.Handle("/metrics", promhttp.Handler())
2021-01-03 14:58:24 -08:00
klog.Infof("listening on %s", *addr)
klog.Fatal(http.ListenAndServe(*addr, nil))
}