add account activity and rewards metrics
This commit is contained in:
parent
04bb2b9232
commit
3952b7738a
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/helium-blockchain-exporter/heliumapi"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
@ -20,7 +21,20 @@ type metricInfo struct {
|
|||
|
||||
// Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics.
|
||||
type Exporter struct {
|
||||
Accounts []string
|
||||
Accounts []Account
|
||||
}
|
||||
|
||||
// Account represents a helium account
|
||||
type Account struct {
|
||||
hotspots []Hotspot
|
||||
hash string
|
||||
lastRewardsUpdate time.Time
|
||||
}
|
||||
|
||||
// Hotspot represent a helium hotspot
|
||||
type Hotspot struct {
|
||||
hash string
|
||||
lastRewardsUpdate time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -47,43 +61,43 @@ var (
|
|||
// helium stats metrics
|
||||
statsValidators = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "validators"),
|
||||
"The number of validators.",
|
||||
prometheus.BuildFQName(namespace, "stats", "validators_total"),
|
||||
"The total number of validators.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsOuis = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "ouis"),
|
||||
"The number of organization unique identifiers.",
|
||||
prometheus.BuildFQName(namespace, "stats", "ouis_total"),
|
||||
"The total number of organization unique identifiers.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsHotspotsDataOnly = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly"),
|
||||
"The number of data only hotspots.",
|
||||
prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly_total"),
|
||||
"The total number of data only hotspots.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsBlocks = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "blocks"),
|
||||
"The height/number of blocks in the blockchain.",
|
||||
prometheus.BuildFQName(namespace, "stats", "blocks_total"),
|
||||
"The total height/number of blocks in the blockchain.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsChallenges = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "challenges"),
|
||||
"The number of challenges.",
|
||||
prometheus.BuildFQName(namespace, "stats", "challenges_total"),
|
||||
"The total number of challenges.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsCities = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
|
@ -95,8 +109,8 @@ var (
|
|||
}
|
||||
statsConsensusGroups = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "consensus_groups"),
|
||||
"The number of consensus groups.",
|
||||
prometheus.BuildFQName(namespace, "stats", "consensus_groups_total"),
|
||||
"The total number of consensus groups.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
|
@ -111,11 +125,11 @@ var (
|
|||
}
|
||||
statsHotspots = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "stats", "hotspots"),
|
||||
"The number of hotspots.",
|
||||
prometheus.BuildFQName(namespace, "stats", "hotspots_total"),
|
||||
"The total number of hotspots.",
|
||||
nil, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
statsTokenSupply = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
|
@ -145,28 +159,28 @@ var (
|
|||
}
|
||||
accountRewardsHnt = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "account", "reward_hnt"),
|
||||
prometheus.BuildFQName(namespace, "account", "rewards_hnt"),
|
||||
"The number of HNT token rewarded to this account.",
|
||||
commonAccountLabels, nil,
|
||||
),
|
||||
prometheus.GaugeValue,
|
||||
}
|
||||
accountDepositsHnt = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"),
|
||||
"The number of HNT token deposited to this account.",
|
||||
commonAccountLabels, nil,
|
||||
),
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
accountWithdrawalsHnt = metricInfo{
|
||||
prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"),
|
||||
"The number of HNT token withdrawn from this account.",
|
||||
commonAccountLabels, nil,
|
||||
),
|
||||
prometheus.CounterValue,
|
||||
}
|
||||
// accountDepositsHnt = metricInfo{
|
||||
// prometheus.NewDesc(
|
||||
// prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"),
|
||||
// "The number of HNT token deposited to this account.",
|
||||
// commonAccountLabels, nil,
|
||||
// ),
|
||||
// prometheus.CounterValue,
|
||||
// }
|
||||
// accountWithdrawalsHnt = metricInfo{
|
||||
// prometheus.NewDesc(
|
||||
// prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"),
|
||||
// "The number of HNT token withdrawn from this account.",
|
||||
// commonAccountLabels, nil,
|
||||
// ),
|
||||
// prometheus.CounterValue,
|
||||
// }
|
||||
|
||||
// helium hotspot metrics
|
||||
)
|
||||
|
@ -190,8 +204,8 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
|||
ch <- accountBalanceHnt.Desc
|
||||
ch <- accountActivity.Desc
|
||||
ch <- accountRewardsHnt.Desc
|
||||
ch <- accountDepositsHnt.Desc
|
||||
ch <- accountWithdrawalsHnt.Desc
|
||||
// ch <- accountDepositsHnt.Desc
|
||||
// ch <- accountWithdrawalsHnt.Desc
|
||||
}
|
||||
|
||||
// Collect fetches the data from the helium blockchain api and delivers them as Prometheus metrics.
|
||||
|
@ -200,7 +214,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||
e.collectOracleMetrics(ch)
|
||||
e.collectStatsMetrics(ch)
|
||||
for _, account := range e.Accounts {
|
||||
e.collectAccountMetrics(ch, account)
|
||||
e.collectAccountMetrics(ch, &account)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,8 +272,20 @@ func (e *Exporter) collectStatsMetrics(ch chan<- prometheus.Metric) {
|
|||
}
|
||||
|
||||
// collectStatsMetrics collect metrics in the account group from the helium api
|
||||
func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account string) {
|
||||
accountForAddress, err := heliumapi.GetAccountForAddress(account)
|
||||
func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account *Account) {
|
||||
accountForAddress, err := heliumapi.GetAccountForAddress(account.hash)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.hash)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
accountRewardTotalsForAddress, err := heliumapi.GetRewardTotalsForAccount(account.hash, &account.lastRewardsUpdate, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
|
@ -267,17 +293,44 @@ func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account st
|
|||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Data.Balance),
|
||||
account,
|
||||
account.hash,
|
||||
)
|
||||
for accType, count := range accountActivityForAddress.Data {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
accountActivity.Desc, accountActivity.Type, float64(count),
|
||||
account.hash, accType,
|
||||
)
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Data.Sum,
|
||||
account.hash,
|
||||
)
|
||||
account.lastRewardsUpdate, err = time.Parse(time.RFC3339, accountRewardTotalsForAddress.Meta.MaxTime)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to parse time \"%v\", value of %v will be bad: %v\n", accountRewardTotalsForAddress.Meta.MaxTime, accountRewardsHnt.Desc.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// NewExporter returns an initialized Exporter
|
||||
func NewExporter(accounts []string) (*Exporter, error) {
|
||||
func NewExporter(accountHashs []string) (*Exporter, error) {
|
||||
accounts := make([]Account, 0)
|
||||
for _, accountHash := range accountHashs {
|
||||
if accountHash != "" {
|
||||
accounts = append(accounts, NewAccount(accountHash))
|
||||
}
|
||||
}
|
||||
return &Exporter{
|
||||
Accounts: accounts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewAccount(hash string) Account {
|
||||
return Account{
|
||||
hash: hash,
|
||||
lastRewardsUpdate: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fHeliumAccounts := flag.String("accounts", "", "A comma-delimited list of helium accounts to scrape.")
|
||||
fMetricsPath := flag.String("metricpath", "/metrics", "The metrics path")
|
||||
|
@ -285,10 +338,7 @@ func main() {
|
|||
fListenPort := flag.String("listenPort", "9111", "The http server listen port")
|
||||
flag.Parse()
|
||||
|
||||
heliumAccounts := make([]string, 0)
|
||||
if *fHeliumAccounts != "" {
|
||||
heliumAccounts = strings.Split(*fHeliumAccounts, ",")
|
||||
}
|
||||
heliumAccounts := strings.Split(*fHeliumAccounts, ",")
|
||||
serverAddr := *fListenAddress + ":" + *fListenPort
|
||||
|
||||
e, err := NewExporter(heliumAccounts)
|
||||
|
@ -311,6 +361,6 @@ func main() {
|
|||
})
|
||||
|
||||
http.Handle(*fMetricsPath, promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
|
||||
fmt.Printf("exporter listening on %v\n", serverAddr)
|
||||
fmt.Printf("listening on %v\n", serverAddr)
|
||||
http.ListenAndServe(serverAddr, nil)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package heliumapi
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
|
@ -18,6 +19,27 @@ type Account struct {
|
|||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ActivityCounts struct {
|
||||
Data map[string]int
|
||||
}
|
||||
|
||||
type RewardTotal struct {
|
||||
Meta struct {
|
||||
MinTime string `json:"min_time"`
|
||||
MaxTime string `json:"max_time"`
|
||||
} `json:"meta"`
|
||||
|
||||
Data struct {
|
||||
Total float64 `json:"total"`
|
||||
Sum float64 `json:"sum"`
|
||||
Stddev float64 `json:"stddev"`
|
||||
Min float64 `json:"min"`
|
||||
Median float64 `json:"median"`
|
||||
Max float64 `json:"max"`
|
||||
Avg float64 `json:"avg"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func GetAccountForAddress(account string) (*Account, error) {
|
||||
path := "/v1/accounts/" + account
|
||||
|
||||
|
@ -36,3 +58,50 @@ func GetAccountForAddress(account string) (*Account, error) {
|
|||
|
||||
return &respobject, nil
|
||||
}
|
||||
|
||||
func GetActivityCountsForAccount(account string) (*ActivityCounts, error) {
|
||||
path := "/v1/accounts/" + account + "/activity/count"
|
||||
|
||||
// query the api
|
||||
respBody, err := getHeliumApi(path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshal the response
|
||||
respobject := ActivityCounts{}
|
||||
err = json.Unmarshal(respBody, &respobject)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err)
|
||||
}
|
||||
|
||||
return &respobject, nil
|
||||
|
||||
}
|
||||
|
||||
func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) {
|
||||
path := "/v1/accounts/" + account + "/rewards/sum"
|
||||
params := map[string]string{}
|
||||
if minTime != nil {
|
||||
params["min_time"] = minTime.UTC().Format("2006-01-02T15:04:05Z")
|
||||
}
|
||||
if maxTime != nil {
|
||||
params["max_time"] = minTime.UTC().Format("2006-01-02T15:04:05Z")
|
||||
}
|
||||
|
||||
// query the api
|
||||
respBody, err := getHeliumApi(path, ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshal the response
|
||||
respobject := RewardTotal{}
|
||||
err = json.Unmarshal(respBody, &respobject)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err)
|
||||
}
|
||||
|
||||
return &respobject, nil
|
||||
|
||||
}
|
||||
|
|
Reference in New Issue