add hotspot sync and witnesses metrics
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
afd97a9c7f
commit
5b843a7948
97
exporter.go
97
exporter.go
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¶ms)
|
||||
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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Reference in New Issue