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  }