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 (
"flag"
"log"
"math"
"net/http"
"os"
"strconv"
@ -221,6 +222,14 @@ var (
),
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{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "relayed"),
@ -245,6 +254,22 @@ var (
),
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{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "geocode_info"),
@ -309,9 +334,12 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- accountWithdrawalsHnt.Desc
ch <- hotspotUp.Desc
ch <- hotspotSynced.Desc
ch <- hotspotRelayed.Desc
ch <- hotspotBlocks.Desc
ch <- hotspotRewardsScale.Desc
ch <- hotspot5dWitnesses.Desc
ch <- hotspot5dWitnessed.Desc
ch <- hotspotGeocodeInfo.Desc
ch <- hotspotAntennaInfo.Desc
ch <- hotspotActivity.Desc
@ -581,7 +609,10 @@ func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheu
for _, hotspotData := range *hotspotsForAddress {
// 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.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
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()
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
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()
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
func GetHotspotsForAccount(account string) (*[]AccountHotspot, error) {
func GetHotspotsForAccount(account string) (*[]Hotspot, error) {
path := "/v1/accounts/" + account + "/hotspots"
// 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
}
// 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"`
}
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 {
Address string `json:"address"`
Balance int `json:"balance"`
@ -15,11 +55,7 @@ type Account struct {
SpeculativeNonce int `json:"speculative_nonce"`
}
type AccountHotspotsResp struct {
Data []AccountHotspot `json:"data"`
}
type AccountHotspot struct {
type Hotspot struct {
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
TimestampAdded string `json:"timestamp_added"`
@ -57,21 +93,6 @@ type AccountHotspot struct {
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 {
Total float64 `json:"total"`
Sum float64 `json:"sum"`
@ -82,19 +103,11 @@ type RewardTotal struct {
Avg float64 `json:"avg"`
}
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
}
type CurrentOraclePrice struct {
Price int `json:"price"`
Block int `json:"block"`
}
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
}
type BlockchainStats struct {
BlockTime struct {
LastDay struct {