github.com/GoogleCloudPlatform/testgrid@v0.0.174/util/metrics/prometheus/prometheus.go (about)

     1  /*
     2  Copyright 2021 The TestGrid Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package prometheus
    18  
    19  import (
    20  	"net/http"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/GoogleCloudPlatform/testgrid/util/metrics"
    26  	"github.com/prometheus/client_golang/prometheus"
    27  	"github.com/prometheus/client_golang/prometheus/promhttp"
    28  	dto "github.com/prometheus/client_model/go"
    29  )
    30  
    31  // NewFactory constructs a new metrics factory
    32  func NewFactory() metrics.Factory {
    33  	return metrics.Factory{
    34  		NewInt64:    NewInt64,
    35  		NewCounter:  NewCounter,
    36  		NewDuration: NewDuration,
    37  	}
    38  }
    39  
    40  // init sets up the Prometheus endpoint at this port and URL when the package is imported
    41  func init() {
    42  	go func() {
    43  		http.Handle("/metrics", promhttp.Handler())
    44  		http.ListenAndServe(":2112", nil)
    45  	}()
    46  }
    47  
    48  // Valuer extends a metric to include a report on its values.
    49  type Valuer interface {
    50  	metrics.Metric
    51  	Values() map[string]float64
    52  }
    53  
    54  type gaugeMetric struct {
    55  	name   string
    56  	fields map[string]bool
    57  	met    *prometheus.GaugeVec
    58  	lock   sync.RWMutex
    59  }
    60  
    61  func gaugeValue(metric *prometheus.GaugeVec, labels ...string) float64 {
    62  	var m = &dto.Metric{}
    63  	if err := metric.WithLabelValues(labels...).Write(m); err != nil {
    64  		return 0
    65  	}
    66  	return m.Gauge.GetValue()
    67  }
    68  
    69  type int64Metric gaugeMetric
    70  
    71  // NewInt64 creates and registers an Int64 metric with Prometheus.
    72  func NewInt64(name, desc string, fields ...string) metrics.Int64 {
    73  	m := prometheus.NewGaugeVec(prometheus.GaugeOpts{
    74  		Name: name,
    75  		Help: desc,
    76  	}, fields)
    77  	prometheus.MustRegister(m)
    78  	return &int64Metric{
    79  		name:   name,
    80  		fields: map[string]bool{},
    81  		met:    m,
    82  	}
    83  }
    84  
    85  // Name returns the metric's name.
    86  func (m *int64Metric) Name() string {
    87  	return m.name
    88  }
    89  
    90  // Set the metric's value to the given number.
    91  func (m *int64Metric) Set(n int64, fields ...string) {
    92  	m.lock.Lock()
    93  	defer m.lock.Unlock()
    94  	m.met.WithLabelValues(fields...).Set(float64(n))
    95  	m.fields[strings.Join(fields, "|")] = true
    96  }
    97  
    98  // Values returns each field and its current value.
    99  func (m *int64Metric) Values() map[string]float64 {
   100  	values := map[string]float64{}
   101  	m.lock.RLock()
   102  	defer m.lock.RUnlock()
   103  	for fieldStr := range m.fields {
   104  		fields := strings.Split(fieldStr, "|")
   105  		values[fieldStr] = gaugeValue(m.met, fields...)
   106  	}
   107  	return values
   108  }
   109  
   110  type durationMetric gaugeMetric
   111  
   112  // NewDuration returns a prometheus-implemented duration metric
   113  // A GagueVec is used instead of a SummaryVec since it shows changes in duration over time more clearly
   114  func NewDuration(name, desc string, fields ...string) metrics.Duration {
   115  	m := prometheus.NewGaugeVec(prometheus.GaugeOpts{
   116  		Name: name,
   117  		Help: desc,
   118  	}, fields)
   119  	prometheus.MustRegister(m)
   120  	return &durationMetric{
   121  		name:   name,
   122  		fields: map[string]bool{},
   123  		met:    m,
   124  	}
   125  }
   126  
   127  func (m *durationMetric) Name() string {
   128  	return m.name
   129  }
   130  
   131  // Set sets the metric's value to the given duration in seconds
   132  func (m *durationMetric) Set(t time.Duration, fields ...string) {
   133  	m.lock.Lock()
   134  	defer m.lock.Unlock()
   135  	m.met.WithLabelValues(fields...).Set(t.Seconds())
   136  	m.fields[strings.Join(fields, "|")] = true
   137  }
   138  
   139  // Values returns each field and its current value.
   140  func (m *durationMetric) Values() map[string]float64 {
   141  	values := map[string]float64{}
   142  	m.lock.RLock()
   143  	defer m.lock.RUnlock()
   144  	for fieldStr := range m.fields {
   145  		fields := strings.Split(fieldStr, "|")
   146  		values[fieldStr] = gaugeValue(m.met, fields...)
   147  	}
   148  	return values
   149  }
   150  
   151  type counterMetric struct {
   152  	name   string
   153  	fields map[string]bool
   154  	met    *prometheus.CounterVec
   155  	lock   sync.RWMutex
   156  }
   157  
   158  // NewCounter creates and registers a strictly-increasing counter metric with Prometheus.
   159  func NewCounter(name, desc string, fields ...string) metrics.Counter {
   160  	m := prometheus.NewCounterVec(prometheus.CounterOpts{
   161  		Name: name,
   162  		Help: desc,
   163  	}, fields)
   164  	prometheus.MustRegister(m)
   165  	return &counterMetric{
   166  		name:   name,
   167  		fields: map[string]bool{},
   168  		met:    m,
   169  	}
   170  }
   171  
   172  // Name returns the metric's name.
   173  func (m *counterMetric) Name() string {
   174  	return m.name
   175  }
   176  
   177  // Add the given number to the Counter.
   178  func (m *counterMetric) Add(n int64, fields ...string) {
   179  	m.met.WithLabelValues(fields...).Add(float64(n))
   180  	m.lock.Lock()
   181  	defer m.lock.Unlock()
   182  	m.fields[strings.Join(fields, "|")] = true
   183  }
   184  
   185  func counterValue(metric *prometheus.CounterVec, labels ...string) float64 {
   186  	var m = &dto.Metric{}
   187  	if err := metric.WithLabelValues(labels...).Write(m); err != nil {
   188  		return 0
   189  	}
   190  	return m.Counter.GetValue()
   191  }
   192  
   193  // Values returns each field and its current value.
   194  func (m *counterMetric) Values() map[string]float64 {
   195  	values := map[string]float64{}
   196  	m.lock.RLock()
   197  	defer m.lock.RUnlock()
   198  	for fieldStr := range m.fields {
   199  		fields := strings.Split(fieldStr, "|")
   200  		values[fieldStr] = counterValue(m.met, fields...)
   201  	}
   202  	return values
   203  }