Gauge add
[ric-plt/xapp-frame.git] / pkg / xapp / metrics.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20 package xapp
21
22 import (
23         "fmt"
24         "github.com/gorilla/mux"
25         "github.com/prometheus/client_golang/prometheus"
26         "github.com/prometheus/client_golang/prometheus/promauto"
27         "github.com/prometheus/client_golang/prometheus/promhttp"
28         "sync"
29 )
30
31 //-----------------------------------------------------------------------------
32 // Alias
33 //-----------------------------------------------------------------------------
34 type CounterOpts prometheus.Opts
35 type Counter prometheus.Counter
36 type Gauge prometheus.Gauge
37
38 //-----------------------------------------------------------------------------
39 //
40 //-----------------------------------------------------------------------------
41
42 type MetricGroupsCache struct {
43         sync.RWMutex //This is for map locking
44         counters     map[string]Counter
45         gauges       map[string]Gauge
46 }
47
48 func (met *MetricGroupsCache) CIs(metric string) bool {
49         met.RLock()
50         defer met.RUnlock()
51         _, ok := met.counters[metric]
52         return ok
53 }
54
55 func (met *MetricGroupsCache) CGet(metric string) Counter {
56         met.RLock()
57         defer met.RUnlock()
58         return met.counters[metric]
59 }
60
61 func (met *MetricGroupsCache) CInc(metric string) {
62         met.RLock()
63         defer met.RUnlock()
64         met.counters[metric].Inc()
65 }
66
67 func (met *MetricGroupsCache) CAdd(metric string, val float64) {
68         met.RLock()
69         defer met.RUnlock()
70         met.counters[metric].Add(val)
71 }
72
73 func (met *MetricGroupsCache) GIs(metric string) bool {
74         met.RLock()
75         defer met.RUnlock()
76         _, ok := met.gauges[metric]
77         return ok
78 }
79
80 func (met *MetricGroupsCache) GGet(metric string) Gauge {
81         met.RLock()
82         defer met.RUnlock()
83         return met.gauges[metric]
84 }
85
86 func (met *MetricGroupsCache) GSet(metric string, val float64) {
87         met.RLock()
88         defer met.RUnlock()
89         met.gauges[metric].Set(val)
90 }
91
92 func (met *MetricGroupsCache) GAdd(metric string, val float64) {
93         met.RLock()
94         defer met.RUnlock()
95         met.gauges[metric].Add(val)
96 }
97
98 func (met *MetricGroupsCache) GInc(metric string) {
99         met.RLock()
100         defer met.RUnlock()
101         met.gauges[metric].Inc()
102 }
103
104 func (met *MetricGroupsCache) GDec(metric string) {
105         met.RLock()
106         defer met.RUnlock()
107         met.gauges[metric].Dec()
108 }
109
110 func (met *MetricGroupsCache) CombineCounterGroups(srcs ...map[string]Counter) {
111         met.Lock()
112         defer met.Unlock()
113         for _, src := range srcs {
114                 for k, v := range src {
115                         met.counters[k] = v
116                 }
117         }
118 }
119
120 func (met *MetricGroupsCache) CombineGaugeGroups(srcs ...map[string]Gauge) {
121         met.Lock()
122         defer met.Unlock()
123         for _, src := range srcs {
124                 for k, v := range src {
125                         met.gauges[k] = v
126                 }
127         }
128 }
129
130 func NewMetricGroupsCache() *MetricGroupsCache {
131         entry := &MetricGroupsCache{}
132         entry.counters = make(map[string]Counter)
133         entry.gauges = make(map[string]Gauge)
134         return entry
135 }
136
137 //-----------------------------------------------------------------------------
138 // All counters/gauges registered via Metrics instances:
139 // Counter names are build from: namespace, subsystem, metric and possible labels
140 //-----------------------------------------------------------------------------
141 var globalLock sync.Mutex
142 var cache_allcounters map[string]Counter
143 var cache_allgauges map[string]Gauge
144
145 func init() {
146         cache_allcounters = make(map[string]Counter)
147         cache_allgauges = make(map[string]Gauge)
148 }
149
150 //-----------------------------------------------------------------------------
151 //
152 //-----------------------------------------------------------------------------
153 type Metrics struct {
154         Namespace string
155 }
156
157 func NewMetrics(url, namespace string, r *mux.Router) *Metrics {
158         if url == "" {
159                 url = "/ric/v1/metrics"
160         }
161         if namespace == "" {
162                 namespace = "ricxapp"
163         }
164
165         Logger.Info("Serving metrics on: url=%s namespace=%s", url, namespace)
166
167         // Expose 'metrics' endpoint with standard golang metrics used by prometheus
168         r.Handle(url, promhttp.Handler())
169
170         return &Metrics{Namespace: namespace}
171 }
172
173 /*
174  * Helpers
175  */
176 func (m *Metrics) getFullName(opts prometheus.Opts, labels []string) string {
177         labelname := ""
178         for _, lbl := range labels {
179                 if len(labelname) == 0 {
180                         labelname += lbl
181                 } else {
182                         labelname += "_" + lbl
183                 }
184         }
185         return fmt.Sprintf("%s_%s_%s_%s", opts.Namespace, opts.Subsystem, opts.Name, labelname)
186 }
187
188 /*
189  * Handling counters
190  */
191 func (m *Metrics) registerCounter(opts CounterOpts) Counter {
192         Logger.Info("Register new counter with opts: %v", opts)
193         return promauto.NewCounter(prometheus.CounterOpts(opts))
194 }
195
196 func (m *Metrics) RegisterCounterGroup(opts []CounterOpts, subsytem string) (c map[string]Counter) {
197         globalLock.Lock()
198         defer globalLock.Unlock()
199         c = make(map[string]Counter)
200         for _, opt := range opts {
201                 opt.Namespace = m.Namespace
202                 opt.Subsystem = subsytem
203
204                 id := m.getFullName(prometheus.Opts(opt), []string{})
205                 if _, ok := cache_allcounters[id]; !ok {
206                         cache_allcounters[id] = m.registerCounter(opt)
207                 }
208
209                 c[opt.Name] = cache_allcounters[id]
210         }
211
212         return
213 }
214
215 /*
216  * Handling gauges
217  */
218 func (m *Metrics) registerGauge(opts CounterOpts) Gauge {
219         Logger.Info("Register new gauge with opts: %v", opts)
220         return promauto.NewGauge(prometheus.GaugeOpts(opts))
221 }
222
223 func (m *Metrics) RegisterGaugeGroup(opts []CounterOpts, subsytem string) (c map[string]Gauge) {
224         globalLock.Lock()
225         defer globalLock.Unlock()
226         c = make(map[string]Gauge)
227         for _, opt := range opts {
228                 opt.Namespace = m.Namespace
229                 opt.Subsystem = subsytem
230
231                 id := m.getFullName(prometheus.Opts(opt), []string{})
232                 if _, ok := cache_allgauges[id]; !ok {
233                         cache_allgauges[id] = m.registerGauge(opt)
234                 }
235
236                 c[opt.Name] = cache_allgauges[id]
237         }
238
239         return
240 }
241
242 /*
243  * Handling counter vectors
244  *
245  * Example:
246
247         vec := Metric.RegisterCounterVecGroup(
248                 []CounterOpts{
249                         {Name: "counter1", Help: "counter1"},
250                         {Name: "counter2", Help: "counter2"},
251                 },
252                 []string{"host"},
253                 "SUBSYSTEM")
254
255         stat:=Metric.GetCounterGroupFromVects([]string{"localhost:8888"}, vec)
256
257 */
258 type CounterVec struct {
259         Vec  *prometheus.CounterVec
260         Opts CounterOpts
261 }
262
263 func (m *Metrics) registerCounterVec(opts CounterOpts, labelNames []string) *prometheus.CounterVec {
264         Logger.Info("Register new counter vector with opts: %v labelNames: %v", opts, labelNames)
265         return promauto.NewCounterVec(prometheus.CounterOpts(opts), labelNames)
266 }
267
268 func (m *Metrics) RegisterCounterVecGroup(opts []CounterOpts, labelNames []string, subsytem string) (c map[string]CounterVec) {
269         c = make(map[string]CounterVec)
270         for _, opt := range opts {
271                 entry := CounterVec{}
272                 entry.Opts = opt
273                 entry.Opts.Namespace = m.Namespace
274                 entry.Opts.Subsystem = subsytem
275                 entry.Vec = m.registerCounterVec(entry.Opts, labelNames)
276                 c[opt.Name] = entry
277         }
278         return
279 }
280
281 func (m *Metrics) GetCounterGroupFromVectsWithPrefix(prefix string, labels []string, vects ...map[string]CounterVec) (c map[string]Counter) {
282         globalLock.Lock()
283         defer globalLock.Unlock()
284         c = make(map[string]Counter)
285         for _, vec := range vects {
286                 for name, opt := range vec {
287
288                         id := m.getFullName(prometheus.Opts(opt.Opts), labels)
289                         if _, ok := cache_allcounters[id]; !ok {
290                                 Logger.Info("Register new counter from vector with opts: %v labels: %v prefix: %s", opt.Opts, labels, prefix)
291                                 cache_allcounters[id] = opt.Vec.WithLabelValues(labels...)
292                         }
293                         c[prefix+name] = cache_allcounters[id]
294                 }
295         }
296         return
297 }
298
299 func (m *Metrics) GetCounterGroupFromVects(labels []string, vects ...map[string]CounterVec) (c map[string]Counter) {
300         return m.GetCounterGroupFromVectsWithPrefix("", labels, vects...)
301 }
302
303 /*
304  * Handling gauge vectors
305  *
306  * Example:
307
308         vec := Metric.RegisterGaugeVecGroup(
309                 []CounterOpts{
310                         {Name: "gauge1", Help: "gauge1"},
311                         {Name: "gauge2", Help: "gauge2"},
312                 },
313                 []string{"host"},
314                 "SUBSYSTEM")
315
316         stat:=Metric.GetGaugeGroupFromVects([]string{"localhost:8888"},vec)
317
318 */
319 type GaugeVec struct {
320         Vec  *prometheus.GaugeVec
321         Opts CounterOpts
322 }
323
324 func (m *Metrics) registerGaugeVec(opts CounterOpts, labelNames []string) *prometheus.GaugeVec {
325         Logger.Info("Register new gauge vector with opts: %v labelNames: %v", opts, labelNames)
326         return promauto.NewGaugeVec(prometheus.GaugeOpts(opts), labelNames)
327 }
328
329 func (m *Metrics) RegisterGaugeVecGroup(opts []CounterOpts, labelNames []string, subsytem string) (c map[string]GaugeVec) {
330         c = make(map[string]GaugeVec)
331         for _, opt := range opts {
332                 entry := GaugeVec{}
333                 entry.Opts = opt
334                 entry.Opts.Namespace = m.Namespace
335                 entry.Opts.Subsystem = subsytem
336                 entry.Vec = m.registerGaugeVec(entry.Opts, labelNames)
337                 c[opt.Name] = entry
338
339         }
340         return
341 }
342
343 func (m *Metrics) GetGaugeGroupFromVectsWithPrefix(prefix string, labels []string, vects ...map[string]GaugeVec) (c map[string]Gauge) {
344         globalLock.Lock()
345         defer globalLock.Unlock()
346         c = make(map[string]Gauge)
347         for _, vec := range vects {
348                 for name, opt := range vec {
349
350                         id := m.getFullName(prometheus.Opts(opt.Opts), labels)
351                         if _, ok := cache_allgauges[id]; !ok {
352                                 Logger.Info("Register new gauge from vector with opts: %v labels: %v prefix: %s", opt.Opts, labels, prefix)
353                                 cache_allgauges[id] = opt.Vec.WithLabelValues(labels...)
354                         }
355                         c[prefix+name] = cache_allgauges[id]
356                 }
357         }
358         return
359 }
360
361 func (m *Metrics) GetGaugeGroupFromVects(labels []string, vects ...map[string]GaugeVec) (c map[string]Gauge) {
362         return m.GetGaugeGroupFromVectsWithPrefix("", labels, vects...)
363 }