github.com/bartle-stripe/trillian@v1.2.1/monitoring/prometheus/metrics.go (about) 1 // Copyright 2017 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package prometheus provides a Prometheus-based implementation of the 16 // MetricFactory abstraction. 17 package prometheus 18 19 import ( 20 "fmt" 21 "math" 22 23 "github.com/golang/glog" 24 "github.com/google/trillian/monitoring" 25 "github.com/prometheus/client_golang/prometheus" 26 dto "github.com/prometheus/client_model/go" 27 ) 28 29 // MetricFactory allows the creation of Prometheus-based metrics. 30 type MetricFactory struct { 31 Prefix string 32 } 33 34 // NewCounter creates a new Counter object backed by Prometheus. 35 func (pmf MetricFactory) NewCounter(name, help string, labelNames ...string) monitoring.Counter { 36 if len(labelNames) == 0 { 37 counter := prometheus.NewCounter( 38 prometheus.CounterOpts{ 39 Name: pmf.Prefix + name, 40 Help: help, 41 }) 42 prometheus.MustRegister(counter) 43 return &Counter{single: counter} 44 } 45 46 vec := prometheus.NewCounterVec( 47 prometheus.CounterOpts{ 48 Name: pmf.Prefix + name, 49 Help: help, 50 }, 51 labelNames) 52 prometheus.MustRegister(vec) 53 return &Counter{labelNames: labelNames, vec: vec} 54 } 55 56 // NewGauge creates a new Gauge object backed by Prometheus. 57 func (pmf MetricFactory) NewGauge(name, help string, labelNames ...string) monitoring.Gauge { 58 if len(labelNames) == 0 { 59 gauge := prometheus.NewGauge( 60 prometheus.GaugeOpts{ 61 Name: pmf.Prefix + name, 62 Help: help, 63 }) 64 prometheus.MustRegister(gauge) 65 return &Gauge{single: gauge} 66 } 67 vec := prometheus.NewGaugeVec( 68 prometheus.GaugeOpts{ 69 Name: pmf.Prefix + name, 70 Help: help, 71 }, 72 labelNames) 73 prometheus.MustRegister(vec) 74 return &Gauge{labelNames: labelNames, vec: vec} 75 } 76 77 // buckets returns a reasonable range of histogram upper limits for most 78 // latency-in-seconds usecases. 79 func buckets() []float64 { 80 // These parameters give an exponential range from 0.04 seconds to ~1 day. 81 num := 300 82 b := 1.05 83 scale := 0.04 84 85 r := make([]float64, 0, num) 86 for i := 0; i < num; i++ { 87 r = append(r, math.Pow(b, float64(i))*scale) 88 } 89 return r 90 } 91 92 // NewHistogram creates a new Histogram object backed by Prometheus. 93 func (pmf MetricFactory) NewHistogram(name, help string, labelNames ...string) monitoring.Histogram { 94 if len(labelNames) == 0 { 95 histogram := prometheus.NewHistogram( 96 prometheus.HistogramOpts{ 97 Name: pmf.Prefix + name, 98 Help: help, 99 Buckets: buckets(), 100 }) 101 prometheus.MustRegister(histogram) 102 return &Histogram{single: histogram} 103 } 104 vec := prometheus.NewHistogramVec( 105 prometheus.HistogramOpts{ 106 Name: pmf.Prefix + name, 107 Help: help, 108 Buckets: buckets(), 109 }, 110 labelNames) 111 prometheus.MustRegister(vec) 112 return &Histogram{labelNames: labelNames, vec: vec} 113 } 114 115 // Counter is a wrapper around a Prometheus Counter or CounterVec object. 116 type Counter struct { 117 labelNames []string 118 single prometheus.Counter 119 vec *prometheus.CounterVec 120 } 121 122 // Inc adds 1 to a counter. 123 func (m *Counter) Inc(labelVals ...string) { 124 labels, err := labelsFor(m.labelNames, labelVals) 125 if err != nil { 126 glog.Error(err.Error()) 127 return 128 } 129 if m.vec != nil { 130 m.vec.With(labels).Inc() 131 } else { 132 m.single.Inc() 133 } 134 } 135 136 // Add adds the given amount to a counter. 137 func (m *Counter) Add(val float64, labelVals ...string) { 138 labels, err := labelsFor(m.labelNames, labelVals) 139 if err != nil { 140 glog.Error(err.Error()) 141 return 142 } 143 if m.vec != nil { 144 m.vec.With(labels).Add(val) 145 } else { 146 m.single.Add(val) 147 } 148 } 149 150 // Value returns the current amount of a counter. 151 func (m *Counter) Value(labelVals ...string) float64 { 152 labels, err := labelsFor(m.labelNames, labelVals) 153 if err != nil { 154 glog.Error(err.Error()) 155 return 0.0 156 } 157 var metric prometheus.Metric 158 if m.vec != nil { 159 metric = m.vec.With(labels) 160 } else { 161 metric = m.single 162 } 163 var metricpb dto.Metric 164 if err := metric.Write(&metricpb); err != nil { 165 glog.Errorf("failed to Write metric: %v", err) 166 return 0.0 167 } 168 if metricpb.Counter == nil { 169 glog.Errorf("counter field missing") 170 return 0.0 171 } 172 return metricpb.Counter.GetValue() 173 } 174 175 // Gauge is a wrapper around a Prometheus Gauge or GaugeVec object. 176 type Gauge struct { 177 labelNames []string 178 single prometheus.Gauge 179 vec *prometheus.GaugeVec 180 } 181 182 // Inc adds 1 to a gauge. 183 func (m *Gauge) Inc(labelVals ...string) { 184 labels, err := labelsFor(m.labelNames, labelVals) 185 if err != nil { 186 glog.Error(err.Error()) 187 return 188 } 189 if m.vec != nil { 190 m.vec.With(labels).Inc() 191 } else { 192 m.single.Inc() 193 } 194 } 195 196 // Dec subtracts 1 from a gauge. 197 func (m *Gauge) Dec(labelVals ...string) { 198 labels, err := labelsFor(m.labelNames, labelVals) 199 if err != nil { 200 glog.Error(err.Error()) 201 return 202 } 203 if m.vec != nil { 204 m.vec.With(labels).Dec() 205 } else { 206 m.single.Dec() 207 } 208 } 209 210 // Add adds given value to a gauge. 211 func (m *Gauge) Add(val float64, labelVals ...string) { 212 labels, err := labelsFor(m.labelNames, labelVals) 213 if err != nil { 214 glog.Error(err.Error()) 215 return 216 } 217 if m.vec != nil { 218 m.vec.With(labels).Add(val) 219 } else { 220 m.single.Add(val) 221 } 222 } 223 224 // Set sets the value of a gauge. 225 func (m *Gauge) Set(val float64, labelVals ...string) { 226 labels, err := labelsFor(m.labelNames, labelVals) 227 if err != nil { 228 glog.Error(err.Error()) 229 return 230 } 231 if m.vec != nil { 232 m.vec.With(labels).Set(val) 233 } else { 234 m.single.Set(val) 235 } 236 } 237 238 // Value returns the current amount of a gauge. 239 func (m *Gauge) Value(labelVals ...string) float64 { 240 labels, err := labelsFor(m.labelNames, labelVals) 241 if err != nil { 242 glog.Error(err.Error()) 243 return 0.0 244 } 245 var metric prometheus.Metric 246 if m.vec != nil { 247 metric = m.vec.With(labels) 248 } else { 249 metric = m.single 250 } 251 var metricpb dto.Metric 252 if err := metric.Write(&metricpb); err != nil { 253 glog.Errorf("failed to Write metric: %v", err) 254 return 0.0 255 } 256 if metricpb.Gauge == nil { 257 glog.Errorf("gauge field missing") 258 return 0.0 259 } 260 return metricpb.Gauge.GetValue() 261 } 262 263 // Histogram is a wrapper around a Prometheus Histogram or HistogramVec object. 264 type Histogram struct { 265 labelNames []string 266 single prometheus.Histogram 267 vec *prometheus.HistogramVec 268 } 269 270 // Observe adds a single observation to the histogram. 271 func (m *Histogram) Observe(val float64, labelVals ...string) { 272 labels, err := labelsFor(m.labelNames, labelVals) 273 if err != nil { 274 glog.Error(err.Error()) 275 return 276 } 277 if m.vec != nil { 278 m.vec.With(labels).Observe(val) 279 } else { 280 m.single.Observe(val) 281 } 282 } 283 284 // Info returns the count and sum of observations for the histogram. 285 func (m *Histogram) Info(labelVals ...string) (uint64, float64) { 286 labels, err := labelsFor(m.labelNames, labelVals) 287 if err != nil { 288 glog.Error(err.Error()) 289 return 0, 0.0 290 } 291 var metric prometheus.Metric 292 if m.vec != nil { 293 metric = m.vec.With(labels).(prometheus.Metric) 294 } else { 295 metric = m.single 296 } 297 var metricpb dto.Metric 298 if err := metric.Write(&metricpb); err != nil { 299 glog.Errorf("failed to Write metric: %v", err) 300 return 0, 0.0 301 } 302 histVal := metricpb.GetHistogram() 303 if histVal == nil { 304 glog.Errorf("histogram field missing") 305 return 0, 0.0 306 } 307 return histVal.GetSampleCount(), histVal.GetSampleSum() 308 } 309 310 func labelsFor(names, values []string) (prometheus.Labels, error) { 311 if len(names) != len(values) { 312 return nil, fmt.Errorf("got %d (%v) values for %d labels (%v)", len(values), values, len(names), names) 313 } 314 if len(names) == 0 { 315 return nil, nil 316 } 317 labels := make(prometheus.Labels) 318 for i, name := range names { 319 labels[name] = values[i] 320 } 321 return labels, nil 322 }