-
Notifications
You must be signed in to change notification settings - Fork 65
/
apcupsdexporter.go
95 lines (80 loc) · 2.51 KB
/
apcupsdexporter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// 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
}