1
0
Fork 0

add hotspot sync and witnesses metrics
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Massaki Archambault 2021-10-07 10:24:46 -04:00
parent afd97a9c7f
commit 5b843a7948
5 changed files with 207 additions and 32 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"flag" "flag"
"log" "log"
"math"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@ -221,6 +222,14 @@ var (
), ),
prometheus.GaugeValue, prometheus.GaugeValue,
} }
hotspotSynced = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "synced"),
"Whether a hotspot is synced with the blockchain.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspotRelayed = metricInfo{ hotspotRelayed = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "relayed"), prometheus.BuildFQName(namespace, "hotspot", "relayed"),
@ -245,6 +254,22 @@ var (
), ),
prometheus.GaugeValue, prometheus.GaugeValue,
} }
hotspot5dWitnesses = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "5d_witnesses"),
"The number of hotspots that witnessed this hotspot in the last 5 days.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspot5dWitnessed = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "5d_witnessed"),
"The number of hotspots this hotspot witnessed in the last 5 days.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspotGeocodeInfo = metricInfo{ hotspotGeocodeInfo = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "geocode_info"), prometheus.BuildFQName(namespace, "hotspot", "geocode_info"),
@ -309,9 +334,12 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- accountWithdrawalsHnt.Desc ch <- accountWithdrawalsHnt.Desc
ch <- hotspotUp.Desc ch <- hotspotUp.Desc
ch <- hotspotSynced.Desc
ch <- hotspotRelayed.Desc ch <- hotspotRelayed.Desc
ch <- hotspotBlocks.Desc ch <- hotspotBlocks.Desc
ch <- hotspotRewardsScale.Desc ch <- hotspotRewardsScale.Desc
ch <- hotspot5dWitnesses.Desc
ch <- hotspot5dWitnessed.Desc
ch <- hotspotGeocodeInfo.Desc ch <- hotspotGeocodeInfo.Desc
ch <- hotspotAntennaInfo.Desc ch <- hotspotAntennaInfo.Desc
ch <- hotspotActivity.Desc ch <- hotspotActivity.Desc
@ -581,7 +609,10 @@ func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheu
for _, hotspotData := range *hotspotsForAddress { for _, hotspotData := range *hotspotsForAddress {
// collect hotspot metric requiring extra queries in a new routine // collect hotspot metric requiring extra queries in a new routine
wg.Add(2) wg.Add(5)
go e.collectHotspotSyncedMetrics(wg, ch, account, hotspotData)
go e.collectHotspotWitnessesMetrics(wg, ch, account, hotspotData)
go e.collectHotspotWitnessedMetrics(wg, ch, account, hotspotData)
go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData) go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData)
go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData) go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData)
@ -612,8 +643,68 @@ func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheu
} }
} }
// collectSyncedMetrics calculate if the hotspot is in sync with the blockchain
func (e *Exporter) collectHotspotSyncedMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
isSynced := 0.
if hotspotData.Status.Online == "online" && hotspotData.Status.Timestamp != "" {
statusTime, err := time.Parse(time.RFC3339Nano, hotspotData.Status.Timestamp)
if err != nil {
log.Println(err)
return
}
heightAtUpdate, err := heliumapi.GetHeight(&statusTime)
if err != nil {
log.Println(err)
return
}
// if the block height and the height reported by the hotspot is within 10 blocks, we consider it in sync
if math.Abs(float64(hotspotData.Status.Height-heightAtUpdate)) <= 10 {
isSynced = 1.
}
}
ch <- prometheus.MustNewConstMetric(
hotspotSynced.Desc, hotspotActivity.Type, isSynced,
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotWitnessesMetrics collect the total number witnesses of a hotspot in the last 5d
func (e *Exporter) collectHotspotWitnessesMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
hotspotWitnesses, err := heliumapi.GetWitnessesForHotspot(hotspotData.Address)
if err != nil {
log.Println(err)
return
}
ch <- prometheus.MustNewConstMetric(
hotspot5dWitnesses.Desc, hotspotActivity.Type, float64(len(*hotspotWitnesses)),
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotWitnessedMetrics collect the total number hotspots witnessed in the last 5d
func (e *Exporter) collectHotspotWitnessedMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
hotspotWitnessed, err := heliumapi.GetWitnessedForHotspot(hotspotData.Address)
if err != nil {
log.Println(err)
return
}
ch <- prometheus.MustNewConstMetric(
hotspot5dWitnessed.Desc, hotspotActivity.Type, float64(len(*hotspotWitnessed)),
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotActivityMetrics collect the total number of activities executed by a hotspot from the helium api // collectHotspotActivityMetrics collect the total number of activities executed by a hotspot from the helium api
func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) { func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done() defer wg.Done()
hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCounts(hotspotData.Address) hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCounts(hotspotData.Address)
@ -631,7 +722,7 @@ func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- p
} }
// collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api // collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api
func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) { func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done() defer wg.Done()
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil) hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)

View File

@ -110,7 +110,7 @@ func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time
} }
// query https://docs.helium.com/api/blockchain/accounts#hotspots-for-account // query https://docs.helium.com/api/blockchain/accounts#hotspots-for-account
func GetHotspotsForAccount(account string) (*[]AccountHotspot, error) { func GetHotspotsForAccount(account string) (*[]Hotspot, error) {
path := "/v1/accounts/" + account + "/hotspots" path := "/v1/accounts/" + account + "/hotspots"
// query the api // query the api

31
heliumapi/block.go Normal file
View File

@ -0,0 +1,31 @@
package heliumapi
import (
"encoding/json"
"fmt"
"time"
)
// query https://docs.helium.com/api/blockchain/blocks/#height
func GetHeight(maxTime *time.Time) (int, error) {
path := "/v1/blocks/height"
params := map[string]string{}
if maxTime != nil {
params["max_time"] = maxTime.UTC().Format("2006-01-02T15:04:05Z")
}
// query the api
respBody, err := getHeliumApi(path, &params)
if err != nil {
return -1, err
}
// unmarshal the response
respobject := HeightResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return -1, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return respobject.Data.Height, nil
}

View File

@ -52,3 +52,43 @@ func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time
return &respobject.Data, nil return &respobject.Data, nil
} }
// query https://docs.helium.com/api/blockchain/hotspots#witnesses-for-a-hotspot
func GetWitnessesForHotspot(hotspot string) (*[]Hotspot, error) {
path := "/v1/hotspots/" + hotspot + "/witnesses"
// query the api
respBody, err := getHeliumApi(path, nil)
if err != nil {
return nil, err
}
// unmarshal the response
respobject := WitnessesResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return &respobject.Data, nil
}
// query https://docs.helium.com/api/blockchain/hotspots#witnessed-for-a-hotspot
func GetWitnessedForHotspot(hotspot string) (*[]Hotspot, error) {
path := "/v1/hotspots/" + hotspot + "/witnessed"
// query the api
respBody, err := getHeliumApi(path, nil)
if err != nil {
return nil, err
}
// unmarshal the response
respobject := WitnessedResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return &respobject.Data, nil
}

View File

@ -4,6 +4,46 @@ type AccountResp struct {
Data Account `json:"data"` Data Account `json:"data"`
} }
type AccountHotspotsResp struct {
Data []Hotspot `json:"data"`
}
type ActivityCounts map[string]int
type ActivityCountsResp struct {
Data ActivityCounts
}
type RewardTotalResp struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data RewardTotal `json:"data"`
}
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
}
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
}
type WitnessesResp struct {
Data []Hotspot `json:"data"`
}
type HeightResp struct {
Data struct {
Height int `json:"height"`
} `json:"data"`
}
type WitnessedResp struct {
Data []Hotspot `json:"data"`
}
type Account struct { type Account struct {
Address string `json:"address"` Address string `json:"address"`
Balance int `json:"balance"` Balance int `json:"balance"`
@ -15,11 +55,7 @@ type Account struct {
SpeculativeNonce int `json:"speculative_nonce"` SpeculativeNonce int `json:"speculative_nonce"`
} }
type AccountHotspotsResp struct { type Hotspot struct {
Data []AccountHotspot `json:"data"`
}
type AccountHotspot struct {
Lng float64 `json:"lng"` Lng float64 `json:"lng"`
Lat float64 `json:"lat"` Lat float64 `json:"lat"`
TimestampAdded string `json:"timestamp_added"` TimestampAdded string `json:"timestamp_added"`
@ -57,21 +93,6 @@ type AccountHotspot struct {
Address string `json:"address"` Address string `json:"address"`
} }
type ActivityCountsResp struct {
Data ActivityCounts
}
type ActivityCounts map[string]int
type RewardTotalResp struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data RewardTotal `json:"data"`
}
type RewardTotal struct { type RewardTotal struct {
Total float64 `json:"total"` Total float64 `json:"total"`
Sum float64 `json:"sum"` Sum float64 `json:"sum"`
@ -82,19 +103,11 @@ type RewardTotal struct {
Avg float64 `json:"avg"` Avg float64 `json:"avg"`
} }
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
}
type CurrentOraclePrice struct { type CurrentOraclePrice struct {
Price int `json:"price"` Price int `json:"price"`
Block int `json:"block"` Block int `json:"block"`
} }
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
}
type BlockchainStats struct { type BlockchainStats struct {
BlockTime struct { BlockTime struct {
LastDay struct { LastDay struct {