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 }