-
Notifications
You must be signed in to change notification settings - Fork 170
/
exporter.go
150 lines (131 loc) · 3.81 KB
/
exporter.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package sql_exporter
import (
"context"
"flag"
"fmt"
"sync"
"github.com/free/sql_exporter/config"
"github.com/golang/protobuf/proto"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)
var dsnOverride = flag.String("config.data-source-name", "", "Data source name to override the value in the configuration file with.")
// Exporter is a prometheus.Gatherer that gathers SQL metrics from targets and merges them with the default registry.
type Exporter interface {
prometheus.Gatherer
// WithContext returns a (single use) copy of the Exporter, which will use the provided context for Gather() calls.
WithContext(context.Context) Exporter
// Config returns the Exporter's underlying Config object.
Config() *config.Config
}
type exporter struct {
config *config.Config
targets []Target
ctx context.Context
}
// NewExporter returns a new Exporter with the provided config.
func NewExporter(configFile string) (Exporter, error) {
c, err := config.Load(configFile)
if err != nil {
return nil, err
}
// Override the DSN if requested (and in single target mode).
if *dsnOverride != "" {
if len(c.Jobs) > 0 {
return nil, fmt.Errorf("The config.data-source-name flag (value %q) only applies in single target mode", *dsnOverride)
} else {
c.Target.DSN = config.Secret(*dsnOverride)
}
}
var targets []Target
if c.Target != nil {
target, err := NewTarget("", "", string(c.Target.DSN), c.Target.Collectors(), nil, c.Globals)
if err != nil {
return nil, err
}
targets = []Target{target}
} else {
targets = make([]Target, 0, len(c.Jobs)*3)
for _, jc := range c.Jobs {
job, err := NewJob(jc, c.Globals)
if err != nil {
return nil, err
}
targets = append(targets, job.Targets()...)
}
}
return &exporter{
config: c,
targets: targets,
ctx: context.Background(),
}, nil
}
func (e *exporter) WithContext(ctx context.Context) Exporter {
return &exporter{
config: e.config,
targets: e.targets,
ctx: ctx,
}
}
// Gather implements prometheus.Gatherer.
func (e *exporter) Gather() ([]*dto.MetricFamily, error) {
var (
metricChan = make(chan Metric, capMetricChan)
errs prometheus.MultiError
)
var wg sync.WaitGroup
wg.Add(len(e.targets))
for _, t := range e.targets {
go func(target Target) {
defer wg.Done()
target.Collect(e.ctx, metricChan)
}(t)
}
// Wait for all collectors to complete, then close the channel.
go func() {
wg.Wait()
close(metricChan)
}()
// Drain metricChan in case of premature return.
defer func() {
for range metricChan {
}
}()
// Gather.
dtoMetricFamilies := make(map[string]*dto.MetricFamily, 10)
for metric := range metricChan {
dtoMetric := &dto.Metric{}
if err := metric.Write(dtoMetric); err != nil {
errs = append(errs, err)
continue
}
metricDesc := metric.Desc()
dtoMetricFamily, ok := dtoMetricFamilies[metricDesc.Name()]
if !ok {
dtoMetricFamily = &dto.MetricFamily{}
dtoMetricFamily.Name = proto.String(metricDesc.Name())
dtoMetricFamily.Help = proto.String(metricDesc.Help())
switch {
case dtoMetric.Gauge != nil:
dtoMetricFamily.Type = dto.MetricType_GAUGE.Enum()
case dtoMetric.Counter != nil:
dtoMetricFamily.Type = dto.MetricType_COUNTER.Enum()
default:
errs = append(errs, fmt.Errorf("don't know how to handle metric %v", dtoMetric))
continue
}
dtoMetricFamilies[metricDesc.Name()] = dtoMetricFamily
}
dtoMetricFamily.Metric = append(dtoMetricFamily.Metric, dtoMetric)
}
// No need to sort metric families, prometheus.Gatherers will do that for us when merging.
result := make([]*dto.MetricFamily, 0, len(dtoMetricFamilies))
for _, mf := range dtoMetricFamilies {
result = append(result, mf)
}
return result, errs
}
// Config implements Exporter.
func (e *exporter) Config() *config.Config {
return e.config
}