@@ -2,20 +2,23 @@ package metrics
22
33import (
44 "fmt"
5- "github.com/prometheus/client_golang/prometheus"
6- "github.com/prometheus/client_golang/prometheus/promauto"
7- "github.com/prometheus/client_golang/prometheus/promhttp"
85 "net/http"
96 "strings"
7+ "sync"
108 "sync/atomic"
119 "time"
10+
11+ "github.com/prometheus/client_golang/prometheus"
12+ "github.com/prometheus/client_golang/prometheus/promauto"
13+ "github.com/prometheus/client_golang/prometheus/promhttp"
1214)
1315
1416// PrometheusMetricsClient attempts to replicate the functionality of the StatsdMetricsClient, but exposing
1517// the metrics via a http endpoint
1618type PrometheusMetricsClient struct {
1719 endpoint string
1820 metricsTags map [string ]map [string ]string
21+ mu sync.RWMutex
1922 started atomic.Value
2023
2124 counters map [string ]prometheus.CounterVec
@@ -150,62 +153,105 @@ var _ MetricsClientInterface = &PrometheusMetricsClient{}
150153func (mc * PrometheusMetricsClient ) incrementPrometheusCounter (
151154 metric string ,
152155 tags map [string ]string ) {
153- if existingCounter , ok := mc .counters [metric ]; ok {
154- existingCounter .With (tags ).Inc ()
155- } else {
156- counter := promauto .NewCounterVec (prometheus.CounterOpts {
156+ mc .mu .RLock ()
157+ counter , ok := mc .counters [metric ]
158+ mc .mu .RUnlock ()
159+
160+ if ok {
161+ counter .With (tags ).Inc ()
162+ return
163+ }
164+
165+ mc .mu .Lock ()
166+ // double check just in case it was created between the RLock and Lock
167+ if counter , ok = mc .counters [metric ]; ! ok {
168+ counter = * promauto .NewCounterVec (prometheus.CounterOpts {
157169 Name : metric ,
158170 }, mapKeys (tags ))
159- counter .With (tags ).Inc ()
160- mc .counters [metric ] = * counter
171+ mc .counters [metric ] = counter
161172 }
173+ mc .mu .Unlock ()
174+
175+ counter .With (tags ).Inc ()
162176}
163177
164178func (mc * PrometheusMetricsClient ) updatePrometheusGauge (
165179 metric string ,
166180 value float64 ,
167181 tags map [string ]string ) {
168- if existingGauge , ok := mc .gauges [metric ]; ok {
169- existingGauge .With (tags ).Add (value )
170- } else {
171- gauge := promauto .NewGaugeVec (prometheus.GaugeOpts {
182+ mc .mu .RLock ()
183+ gauge , ok := mc .gauges [metric ]
184+ mc .mu .RUnlock ()
185+
186+ if ok {
187+ gauge .With (tags ).Add (value )
188+ return
189+ }
190+
191+ mc .mu .Lock ()
192+ // double check just in case it was created between the RLock and Lock
193+ if gauge , ok = mc .gauges [metric ]; ! ok {
194+ gauge = * promauto .NewGaugeVec (prometheus.GaugeOpts {
172195 Name : metric ,
173196 }, mapKeys (tags ))
174- gauge .With (tags ).Add (value )
175- mc .gauges [metric ] = * gauge
197+ mc .gauges [metric ] = gauge
176198 }
199+ mc .mu .Unlock ()
200+
201+ gauge .With (tags ).Add (value )
177202}
178203
179204func (mc * PrometheusMetricsClient ) observeValuePrometheusHistogram (
180205 metric string ,
181206 value float64 ,
182207 tags map [string ]string ) {
183- if existingHistogram , ok := mc .histograms [metric ]; ok {
184- existingHistogram .With (tags ).Observe (value )
185- } else {
186- histogram := promauto .NewHistogramVec (prometheus.HistogramOpts {
208+ mc .mu .RLock ()
209+ histogram , ok := mc .histograms [metric ]
210+ mc .mu .RUnlock ()
211+
212+ if ok {
213+ histogram .With (tags ).Observe (value )
214+ return
215+ }
216+
217+ mc .mu .Lock ()
218+ // double check just in case it was created between the RLock and Lock
219+ if histogram , ok = mc .histograms [metric ]; ! ok {
220+ histogram = * promauto .NewHistogramVec (prometheus.HistogramOpts {
187221 Name : metric ,
188222 }, mapKeys (tags ))
189- histogram .With (tags ).Observe (value )
190- mc .histograms [metric ] = * histogram
223+ mc .histograms [metric ] = histogram
191224 }
225+ mc .mu .Unlock ()
226+
227+ histogram .With (tags ).Observe (value )
192228}
193229
194230func (mc * PrometheusMetricsClient ) observeValuePrometheusTimer (
195231 metric string ,
196232 duration time.Duration ,
197233 tags map [string ]string ) {
198234 timerMetric := metric + "_timer"
199- if existingHistogram , ok := mc .timings [timerMetric ]; ok {
200- existingHistogram .With (tags ).Observe (float64 (duration .Milliseconds ()))
201- } else {
202- histogram := promauto .NewHistogramVec (prometheus.HistogramOpts {
203- Name : timerMetric ,
204- }, mapKeys (tags ))
235+ mc .mu .RLock ()
236+ histogram , ok := mc .timings [timerMetric ]
237+ mc .mu .RUnlock ()
205238
239+ if ok {
206240 histogram .With (tags ).Observe (float64 (duration .Milliseconds ()))
207- mc .timings [timerMetric ] = * histogram
241+ return
242+ }
243+
244+ mc .mu .Lock ()
245+ // double check just in case it was created between the RLock and Lock
246+ if histogram , ok = mc .timings [timerMetric ]; ! ok {
247+ histogram = * promauto .NewHistogramVec (prometheus.HistogramOpts {
248+ Name : timerMetric ,
249+ }, mapKeys (tags ))
250+ mc .timings [timerMetric ] = histogram
208251 }
252+ mc .mu .Unlock ()
253+
254+ histogram .With (tags ).Observe (float64 (duration .Milliseconds ()))
209255}
210256
211257func mapKeys [T comparable , U any ](inputMap map [T ]U ) []T {
0 commit comments