...
1
16
17 package prometheusextension
18
19 import (
20 "errors"
21 "time"
22
23 "github.com/prometheus/client_golang/prometheus"
24 dto "github.com/prometheus/client_model/go"
25 )
26
27
28
29
30
31 type GaugeOps interface {
32
33 Set(float64)
34
35 Inc()
36
37 Dec()
38
39 Add(float64)
40
41 Sub(float64)
42
43
44 SetToCurrentTime()
45 }
46
47
48
49
50
51 type TimingHistogram interface {
52 prometheus.Metric
53 prometheus.Collector
54 GaugeOps
55 }
56
57
58 type TimingHistogramOpts struct {
59 Namespace string
60 Subsystem string
61 Name string
62 Help string
63 ConstLabels prometheus.Labels
64
65
66
67
68
69
70
71 Buckets []float64
72
73
74 InitialValue float64
75 }
76
77
78 func NewTimingHistogram(opts TimingHistogramOpts) (TimingHistogram, error) {
79 return NewTestableTimingHistogram(time.Now, opts)
80 }
81
82
83 func NewTestableTimingHistogram(nowFunc func() time.Time, opts TimingHistogramOpts) (TimingHistogram, error) {
84 desc := prometheus.NewDesc(
85 prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
86 wrapTimingHelp(opts.Help),
87 nil,
88 opts.ConstLabels,
89 )
90 return newTimingHistogram(nowFunc, desc, opts)
91 }
92
93 func wrapTimingHelp(given string) string {
94 return "EXPERIMENTAL: " + given
95 }
96
97 func newTimingHistogram(nowFunc func() time.Time, desc *prometheus.Desc, opts TimingHistogramOpts, variableLabelValues ...string) (TimingHistogram, error) {
98 allLabelsM := prometheus.Labels{}
99 allLabelsS := prometheus.MakeLabelPairs(desc, variableLabelValues)
100 for _, pair := range allLabelsS {
101 if pair == nil || pair.Name == nil || pair.Value == nil {
102 return nil, errors.New("prometheus.MakeLabelPairs returned a nil")
103 }
104 allLabelsM[*pair.Name] = *pair.Value
105 }
106 weighted, err := newWeightedHistogram(desc, WeightedHistogramOpts{
107 Namespace: opts.Namespace,
108 Subsystem: opts.Subsystem,
109 Name: opts.Name,
110 Help: opts.Help,
111 ConstLabels: allLabelsM,
112 Buckets: opts.Buckets,
113 }, variableLabelValues...)
114 if err != nil {
115 return nil, err
116 }
117 return &timingHistogram{
118 nowFunc: nowFunc,
119 weighted: weighted,
120 lastSetTime: nowFunc(),
121 value: opts.InitialValue,
122 }, nil
123 }
124
125 type timingHistogram struct {
126 nowFunc func() time.Time
127 weighted *weightedHistogram
128
129
130
131 lastSetTime time.Time
132 value float64
133 }
134
135 var _ TimingHistogram = &timingHistogram{}
136
137 func (th *timingHistogram) Set(newValue float64) {
138 th.update(func(float64) float64 { return newValue })
139 }
140
141 func (th *timingHistogram) Inc() {
142 th.update(func(oldValue float64) float64 { return oldValue + 1 })
143 }
144
145 func (th *timingHistogram) Dec() {
146 th.update(func(oldValue float64) float64 { return oldValue - 1 })
147 }
148
149 func (th *timingHistogram) Add(delta float64) {
150 th.update(func(oldValue float64) float64 { return oldValue + delta })
151 }
152
153 func (th *timingHistogram) Sub(delta float64) {
154 th.update(func(oldValue float64) float64 { return oldValue - delta })
155 }
156
157 func (th *timingHistogram) SetToCurrentTime() {
158 th.update(func(oldValue float64) float64 { return th.nowFunc().Sub(time.Unix(0, 0)).Seconds() })
159 }
160
161 func (th *timingHistogram) update(updateFn func(float64) float64) {
162 th.weighted.lock.Lock()
163 defer th.weighted.lock.Unlock()
164 now := th.nowFunc()
165 delta := now.Sub(th.lastSetTime)
166 value := th.value
167 if delta > 0 {
168 th.weighted.observeWithWeightLocked(value, uint64(delta))
169 th.lastSetTime = now
170 }
171 th.value = updateFn(value)
172 }
173
174 func (th *timingHistogram) Desc() *prometheus.Desc {
175 return th.weighted.Desc()
176 }
177
178 func (th *timingHistogram) Write(dest *dto.Metric) error {
179 th.Add(0)
180 return th.weighted.Write(dest)
181 }
182
183 func (th *timingHistogram) Describe(ch chan<- *prometheus.Desc) {
184 ch <- th.weighted.Desc()
185 }
186
187 func (th *timingHistogram) Collect(ch chan<- prometheus.Metric) {
188 ch <- th
189 }
190
View as plain text