96 lines
2.5 KiB
Go
96 lines
2.5 KiB
Go
// Package apcupsdexporter provides the Exporter type used in the
|
|
// apcupsd_exporter Prometheus exporter.
|
|
package apcupsdexporter
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/mdlayher/apcupsd"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// TODO(mdlayher): rework this with metricslite.
|
|
|
|
const (
|
|
// namespace is the top-level namespace for this apcupsd exporter.
|
|
namespace = "apcupsd"
|
|
)
|
|
|
|
// An Exporter is a Prometheus exporter for apcupsd metrics.
|
|
// It wraps all apcupsd metrics collectors and provides a single global
|
|
// exporter which can serve metrics.
|
|
//
|
|
// It implements the prometheus.Collector interface in order to register
|
|
// with Prometheus.
|
|
type Exporter struct {
|
|
clientFn ClientFunc
|
|
}
|
|
|
|
var _ prometheus.Collector = &Exporter{}
|
|
|
|
// A ClientFunc is a function which can return an apcupsd NIS client.
|
|
// ClientFuncs are invoked on each Prometheus scrape, so that connections
|
|
// can be short-lived and less likely to time out or fail.
|
|
type ClientFunc func(ctx context.Context) (*apcupsd.Client, error)
|
|
|
|
// New creates a new Exporter which collects metrics by creating a apcupsd
|
|
// client using the input ClientFunc.
|
|
func New(fn ClientFunc) *Exporter {
|
|
return &Exporter{
|
|
clientFn: fn,
|
|
}
|
|
}
|
|
|
|
// Describe sends all the descriptors of the collectors included to
|
|
// the provided channel.
|
|
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
|
// Don't care, we'll get this error on Collect.
|
|
_ = e.withCollectors(func(cs []prometheus.Collector) {
|
|
for _, c := range cs {
|
|
c.Describe(ch)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Collect sends the collected metrics from each of the collectors to
|
|
// prometheus.
|
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|
err := e.withCollectors(func(cs []prometheus.Collector) {
|
|
for _, c := range cs {
|
|
c.Collect(ch)
|
|
}
|
|
})
|
|
// This is a hack but it allows us to report failure to dial without
|
|
// reworking significant portions of the code.
|
|
if err != nil {
|
|
log.Println(err)
|
|
ch <- prometheus.NewInvalidMetric(NewUPSCollector(nil).Info, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// withCollectors sets up an apcupsd client and creates a set of prometheus
|
|
// collectors. It invokes the input closure and then cleans up after the
|
|
// closure returns.
|
|
func (e *Exporter) withCollectors(fn func(cs []prometheus.Collector)) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
c, err := e.clientFn(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating apcupsd client: %v", err)
|
|
}
|
|
defer c.Close()
|
|
|
|
cs := []prometheus.Collector{
|
|
NewUPSCollector(c),
|
|
}
|
|
|
|
fn(cs)
|
|
|
|
return nil
|
|
}
|