promhippie / hetzner_exporter

Prometheus exporter for Hetzner
https://promhippie.github.io/hetzner_exporter/
Apache License 2.0
27 stars 2 forks source link

Export products #3

Closed tboerger closed 3 years ago

tboerger commented 6 years ago

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...,
        )
    }
}
tboerger commented 3 years ago

Not implementing that in the near future.