package main import ( "fmt" "log" "net/http" "github.com/helium-blockchain-exporter/heliumapi" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) // metricInfo is a metric exported by the helium blockchain exporter type metricInfo struct { Desc *prometheus.Desc Type prometheus.ValueType } // Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics. type Exporter struct { } const ( namespace = "helium" ) var ( // exporter metrics // helium oracle metrics oraclePrice = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "oracle", "price_usd"), "The oracle price of an HNT token in USD.", nil, nil, ), prometheus.GaugeValue, } // helium stats metrics statsValidators = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "validators"), "The number of hotspots.", nil, nil, ), prometheus.GaugeValue, } statsOuis = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "ouis"), "The number of organization unique identifiers.", nil, nil, ), prometheus.GaugeValue, } statsHotspotsDataOnly = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly"), "The number of data only hotspots.", nil, nil, ), prometheus.GaugeValue, } statsBlocks = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "blocks"), "The height/number of blocks in the blockchain.", nil, nil, ), prometheus.GaugeValue, } statsChallenges = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "challenges"), "The number of challenges.", nil, nil, ), prometheus.GaugeValue, } statsCities = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "cities"), "The number of cities with at least one helium hotspot.", nil, nil, ), prometheus.GaugeValue, } statsConsensusGroups = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "consensus_groups"), "The number of consensus groups.", nil, nil, ), prometheus.GaugeValue, } statsCountries = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "countries"), "The number of countries with at least on helium hotspot.", nil, nil, ), prometheus.GaugeValue, } statsHotspots = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "hotspots"), "The number of hotspots.", nil, nil, ), prometheus.GaugeValue, } statsTokenSupply = metricInfo{ prometheus.NewDesc( prometheus.BuildFQName(namespace, "stats", "token_supply"), "The total supply of HNT tokens in circulation.", nil, nil, ), prometheus.GaugeValue, } // helium account metrics // helium hotspot metrics ) // Describe describes all the metrics ever exported by the helium blockchain exporter. // implements prometheus.Collector. func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { ch <- oraclePrice.Desc ch <- statsValidators.Desc ch <- statsOuis.Desc ch <- statsHotspotsDataOnly.Desc ch <- statsBlocks.Desc ch <- statsChallenges.Desc ch <- statsCities.Desc ch <- statsConsensusGroups.Desc ch <- statsCountries.Desc ch <- statsHotspots.Desc ch <- statsTokenSupply.Desc } // Collect fetches the data from the helium blockchain api and delivers them as Prometheus metrics. // implements prometheus.Collector. func (e *Exporter) Collect(ch chan<- prometheus.Metric) { e.collectOracleMetrics(ch) e.collectStatsMetrics(ch) } func (e *Exporter) collectOracleMetrics(ch chan<- prometheus.Metric) { currentOraclePrice, err := heliumapi.GetCurrentOraclePrice() if err != nil { fmt.Println(err) return } ch <- prometheus.MustNewConstMetric( oraclePrice.Desc, oraclePrice.Type, float64(currentOraclePrice.Data.Price)/100000000, ) } func (e *Exporter) collectStatsMetrics(ch chan<- prometheus.Metric) { blockchainStats, err := heliumapi.GetBlockchainStats() if err != nil { fmt.Println(err) return } ch <- prometheus.MustNewConstMetric( statsValidators.Desc, statsValidators.Type, float64(blockchainStats.Data.Counts.Validators), ) ch <- prometheus.MustNewConstMetric( statsOuis.Desc, statsOuis.Type, float64(blockchainStats.Data.Counts.Ouis), ) ch <- prometheus.MustNewConstMetric( statsHotspotsDataOnly.Desc, statsHotspotsDataOnly.Type, float64(blockchainStats.Data.Counts.HotspotsDataonly), ) ch <- prometheus.MustNewConstMetric( statsBlocks.Desc, statsBlocks.Type, float64(blockchainStats.Data.Counts.Blocks), ) ch <- prometheus.MustNewConstMetric( statsChallenges.Desc, statsChallenges.Type, float64(blockchainStats.Data.Counts.Challenges), ) ch <- prometheus.MustNewConstMetric( statsCities.Desc, statsCities.Type, float64(blockchainStats.Data.Counts.Cities), ) ch <- prometheus.MustNewConstMetric( statsConsensusGroups.Desc, statsConsensusGroups.Type, float64(blockchainStats.Data.Counts.ConsensusGroups), ) ch <- prometheus.MustNewConstMetric( statsCountries.Desc, statsCountries.Type, float64(blockchainStats.Data.Counts.Countries), ) ch <- prometheus.MustNewConstMetric( statsHotspots.Desc, statsHotspots.Type, float64(blockchainStats.Data.Counts.Hotspots), ) ch <- prometheus.MustNewConstMetric( statsTokenSupply.Desc, statsTokenSupply.Type, blockchainStats.Data.TokenSupply, ) } // NewExporter returns an initialized Exporter func NewExporter() (*Exporter, error) { return &Exporter{}, nil } func main() { metricsPath := "/metrics" e, err := NewExporter() if err != nil { log.Fatalf("failed to start exporter: %s", err.Error()) } r := prometheus.NewRegistry() r.MustRegister(e) // setup http route http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(` Helium blockchain exporter

Helium blockchain exporter

Metrics

`)) }) http.Handle(metricsPath, promhttp.HandlerFor(r, promhttp.HandlerOpts{})) http.ListenAndServe(":9111", nil) }