The upstream client library is not correctly working for products, we should fix that and implement the exporter afterward. Here is an example how the collector could look like:
package exporter
import (
"fmt"
"strconv"
"time"
"github.com/appscode/go-hetzner"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/dustin/go-humanize"
)
// ProductCollector collects metrics about the SSH keys.
type ProductCollector struct {
client *hetzner.Client
logger log.Logger
failures *prometheus.CounterVec
duration *prometheus.HistogramVec
timeout time.Duration
Up *prometheus.Desc
Traffic *prometheus.Desc
SetupVat *prometheus.Desc
Setup *prometheus.Desc
PriceVat *prometheus.Desc
Price *prometheus.Desc
}
// NewProductCollector returns a new ProductCollector.
func NewProductCollector(logger log.Logger, client *hetzner.Client, failures *prometheus.CounterVec, duration *prometheus.HistogramVec, timeout time.Duration) *ProductCollector {
failures.WithLabelValues("product").Add(0)
labels := []string{"id", "name"}
return &ProductCollector{
client: client,
logger: logger,
failures: failures,
duration: duration,
timeout: timeout,
Up: prometheus.NewDesc(
"hetzner_product_up",
"1 if the product is available",
labels,
nil,
),
Traffic: prometheus.NewDesc(
"hetzner_product_traffic_bytes",
"Show the inclusive traffic for this product",
labels,
nil,
),
SetupVat: prometheus.NewDesc(
"hetzner_product_setup_vat_euro",
"Setup VAT for the product in €",
labels,
nil,
),
Setup: prometheus.NewDesc(
"hetzner_product_setup_euro",
"Setup fee for the product in €",
labels,
nil,
),
PriceVat: prometheus.NewDesc(
"hetzner_product_price_vat_euro",
"Monthly VAT for the product in €",
labels,
nil,
),
Price: prometheus.NewDesc(
"hetzner_product_price_euro",
"Monthly fee for the product in €",
labels,
nil,
),
}
}
// Describe sends the super-set of all possible descriptors of metrics collected by this Collector.
func (c *ProductCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.Up
ch <- c.Traffic
ch <- c.SetupVat
ch <- c.Setup
ch <- c.PriceVat
ch <- c.Price
}
// Collect is called by the Prometheus registry when collecting metrics.
func (c *ProductCollector) Collect(ch chan<- prometheus.Metric) {
now := time.Now()
products, _, err := c.client.WithTimeout(c.timeout).Ordering.ListProducts()
c.duration.WithLabelValues("product").Observe(time.Since(now).Seconds())
if err != nil {
level.Error(c.logger).Log(
"msg", "Failed to fetch products",
"err", err,
)
c.failures.WithLabelValues("product").Inc()
return
}
level.Debug(c.logger).Log(
"msg", "Fetched products",
"count", len(products),
)
for _, product := range products {
var (
traffic float64
setup float64
setupVat float64
price float64
priceVat float64
)
labels := []string{
product.ID,
product.Name,
}
ch <- prometheus.MustNewConstMetric(
c.Up,
prometheus.GaugeValue,
1.0,
labels...,
)
if num, err := humanize.ParseBytes(product.Traffic); err == nil {
traffic = float64(num)
}
ch <- prometheus.MustNewConstMetric(
c.Traffic,
prometheus.GaugeValue,
traffic,
labels...,
)
if num, err := strconv.ParseFloat(product.PriceSetup, 64); err == nil {
setup = num
}
ch <- prometheus.MustNewConstMetric(
c.Setup,
prometheus.GaugeValue,
setup,
labels...,
)
if num, err := strconv.ParseFloat(product.PriceSetupVat, 64); err == nil {
setupVat = num
}
ch <- prometheus.MustNewConstMetric(
c.SetupVat,
prometheus.GaugeValue,
setupVat,
labels...,
)
if num, err := strconv.ParseFloat(product.Price, 64); err == nil {
price = num
}
ch <- prometheus.MustNewConstMetric(
c.Price,
prometheus.GaugeValue,
price,
labels...,
)
if num, err := strconv.ParseFloat(product.PriceVat, 64); err == nil {
priceVat = num
}
ch <- prometheus.MustNewConstMetric(
c.PriceVat,
prometheus.GaugeValue,
priceVat,
labels...,
)
}
}
The upstream client library is not correctly working for products, we should fix that and implement the exporter afterward. Here is an example how the collector could look like: