RIC:1060: Change in PTL
[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         "sync"
25
26         "github.com/gorilla/mux"
27         "github.com/prometheus/client_golang/prometheus"
28         "github.com/prometheus/client_golang/prometheus/promauto"
29         "github.com/prometheus/client_golang/prometheus/promhttp"
30 )
31
32 //-----------------------------------------------------------------------------
33 //
34 //-----------------------------------------------------------------------------
35 func strSliceCompare(a, b []string) bool {
36         if len(a) != len(b) {
37                 return false
38         }
39         for i, v := range a {
40                 if v != b[i] {
41                         return false
42                 }
43         }
44         return true
45 }
46
47 //-----------------------------------------------------------------------------
48 // Alias
49 //-----------------------------------------------------------------------------
50 type CounterOpts prometheus.Opts
51 type Counter prometheus.Counter
52 type Gauge prometheus.Gauge
53
54 type CounterVec struct {
55         Vec    *prometheus.CounterVec
56         Opts   CounterOpts
57         Labels []string
58 }
59
60 type GaugeVec struct {
61         Vec    *prometheus.GaugeVec
62         Opts   CounterOpts
63         Labels []string
64 }
65
66 //-----------------------------------------------------------------------------
67 //
68 //-----------------------------------------------------------------------------
69 type MetricGroupsCacheCounterRegisterer interface {
70         RegisterCounter(CounterOpts) Counter
71 }
72
73 type MetricGroupsCacheCounterRegistererFunc func(CounterOpts) Counter
74
75 func (fn MetricGroupsCacheCounterRegistererFunc) RegisterCounter(copts CounterOpts) Counter {
76         return fn(copts)
77 }
78
79 //-----------------------------------------------------------------------------
80 //
81 //-----------------------------------------------------------------------------
82 type MetricGroupsCacheGaugeRegisterer interface {
83         RegisterGauge(CounterOpts) Gauge
84 }
85
86 type MetricGroupsCacheGaugeRegistererFunc func(CounterOpts) Gauge
87
88 func (fn MetricGroupsCacheGaugeRegistererFunc) RegisterGauge(copts CounterOpts) Gauge {
89         return fn(copts)
90 }
91
92 //-----------------------------------------------------------------------------
93 //
94 //-----------------------------------------------------------------------------
95
96 type MetricGroupsCache struct {
97         sync.RWMutex //This is for map locking
98         counters     map[string]Counter
99         gauges       map[string]Gauge
100         regcnt       MetricGroupsCacheCounterRegisterer
101         reggau       MetricGroupsCacheGaugeRegisterer
102 }
103
104 func (met *MetricGroupsCache) Registerer(regcnt MetricGroupsCacheCounterRegisterer, reggau MetricGroupsCacheGaugeRegisterer) {
105         met.regcnt = regcnt
106         met.reggau = reggau
107 }
108
109 func (met *MetricGroupsCache) cReg(metric string) Counter {
110         if met.regcnt != nil {
111                 cntr := met.regcnt.RegisterCounter(CounterOpts{Name: metric, Help: "Amount of " + metric + "(auto)"})
112                 met.counters[metric] = cntr
113                 return cntr
114         }
115         return nil
116 }
117 func (met *MetricGroupsCache) gReg(metric string) Gauge {
118         if met.reggau != nil {
119                 gaug := met.reggau.RegisterGauge(CounterOpts{Name: metric, Help: "Amount of " + metric + "(auto)"})
120                 met.gauges[metric] = gaug
121                 return gaug
122         }
123         return nil
124 }
125
126 func (met *MetricGroupsCache) CIs(metric string) bool {
127         met.Lock()
128         defer met.Unlock()
129         _, ok := met.counters[metric]
130         return ok
131 }
132
133 func (met *MetricGroupsCache) CGet(metric string) Counter {
134         met.Lock()
135         defer met.Unlock()
136         cntr, ok := met.counters[metric]
137         if !ok {
138                 cntr = met.cReg(metric)
139         }
140         return cntr
141 }
142
143 func (met *MetricGroupsCache) CInc(metric string) {
144         met.Lock()
145         defer met.Unlock()
146         cntr, ok := met.counters[metric]
147         if !ok {
148                 cntr = met.cReg(metric)
149         }
150         cntr.Inc()
151 }
152
153 func (met *MetricGroupsCache) CAdd(metric string, val float64) {
154         met.Lock()
155         defer met.Unlock()
156         cntr, ok := met.counters[metric]
157         if !ok {
158                 cntr = met.cReg(metric)
159         }
160         cntr.Add(val)
161 }
162
163 func (met *MetricGroupsCache) GIs(metric string) bool {
164         met.Lock()
165         defer met.Unlock()
166         _, ok := met.gauges[metric]
167         return ok
168 }
169
170 func (met *MetricGroupsCache) GGet(metric string) Gauge {
171         met.Lock()
172         defer met.Unlock()
173         gaug, ok := met.gauges[metric]
174         if !ok {
175                 gaug = met.gReg(metric)
176         }
177         return gaug
178 }
179
180 func (met *MetricGroupsCache) GSet(metric string, val float64) {
181         met.Lock()
182         defer met.Unlock()
183         gaug, ok := met.gauges[metric]
184         if !ok {
185                 gaug = met.gReg(metric)
186         }
187         gaug.Set(val)
188 }
189
190 func (met *MetricGroupsCache) GAdd(metric string, val float64) {
191         met.Lock()
192         defer met.Unlock()
193         gaug, ok := met.gauges[metric]
194         if !ok {
195                 gaug = met.gReg(metric)
196         }
197         gaug.Add(val)
198 }
199
200 func (met *MetricGroupsCache) GInc(metric string) {
201         met.Lock()
202         defer met.Unlock()
203         gaug, ok := met.gauges[metric]
204         if !ok {
205                 gaug = met.gReg(metric)
206         }
207         gaug.Inc()
208 }
209
210 func (met *MetricGroupsCache) GDec(metric string) {
211         met.Lock()
212         defer met.Unlock()
213         gaug, ok := met.gauges[metric]
214         if !ok {
215                 gaug = met.gReg(metric)
216         }
217         gaug.Dec()
218 }
219
220 func (met *MetricGroupsCache) combineCounterGroupsWithPrefix(prefix string, srcs ...map[string]Counter) {
221         for _, src := range srcs {
222                 for k, v := range src {
223                         met.counters[prefix+k] = v
224                 }
225         }
226 }
227
228 func (met *MetricGroupsCache) CombineCounterGroupsWithPrefix(prefix string, srcs ...map[string]Counter) {
229         met.Lock()
230         defer met.Unlock()
231         met.combineCounterGroupsWithPrefix(prefix, srcs...)
232 }
233
234 func (met *MetricGroupsCache) CombineCounterGroups(srcs ...map[string]Counter) {
235         met.Lock()
236         defer met.Unlock()
237         met.combineCounterGroupsWithPrefix("", srcs...)
238 }
239
240 func (met *MetricGroupsCache) combineGaugeGroupsWithPrefix(prefix string, srcs ...map[string]Gauge) {
241         for _, src := range srcs {
242                 for k, v := range src {
243                         met.gauges[prefix+k] = v
244                 }
245         }
246 }
247
248 func (met *MetricGroupsCache) CombineGaugeGroupsWithPrefix(prefix string, srcs ...map[string]Gauge) {
249         met.Lock()
250         defer met.Unlock()
251         met.combineGaugeGroupsWithPrefix(prefix, srcs...)
252 }
253
254 func (met *MetricGroupsCache) CombineGaugeGroups(srcs ...map[string]Gauge) {
255         met.Lock()
256         defer met.Unlock()
257         met.combineGaugeGroupsWithPrefix("", srcs...)
258 }
259
260 func NewMetricGroupsCache() *MetricGroupsCache {
261         entry := &MetricGroupsCache{}
262         entry.counters = make(map[string]Counter)
263         entry.gauges = make(map[string]Gauge)
264         entry.regcnt = nil
265         entry.reggau = nil
266         return entry
267 }
268
269 func NewMetricGroupsCacheWithRegisterers(regcnt MetricGroupsCacheCounterRegisterer, reggau MetricGroupsCacheGaugeRegisterer) *MetricGroupsCache {
270         entry := NewMetricGroupsCache()
271         entry.regcnt = regcnt
272         entry.reggau = reggau
273         return entry
274 }
275
276 //-----------------------------------------------------------------------------
277 // All counters/gauges registered via Metrics instances:
278 // Counter names are build from: namespace, subsystem, metric and possible labels
279 //-----------------------------------------------------------------------------
280 var globalLock sync.Mutex
281 var cache_allcounters map[string]Counter
282 var cache_allgauges map[string]Gauge
283 var cache_allcountervects map[string]CounterVec
284 var cache_allgaugevects map[string]GaugeVec
285
286 func init() {
287         cache_allcounters = make(map[string]Counter)
288         cache_allgauges = make(map[string]Gauge)
289         cache_allcountervects = make(map[string]CounterVec)
290         cache_allgaugevects = make(map[string]GaugeVec)
291 }
292
293 //-----------------------------------------------------------------------------
294 //
295 //-----------------------------------------------------------------------------
296 type Metrics struct {
297         Namespace string
298 }
299
300 func NewMetrics(url, namespace string, r *mux.Router) *Metrics {
301         if url == "" {
302                 url = "/ric/v1/metrics"
303         }
304         Logger.Info("Serving metrics on: url=%s namespace=%s", url, namespace)
305
306         // Expose 'metrics' endpoint with standard golang metrics used by prometheus
307         r.Handle(url, promhttp.Handler())
308
309         return &Metrics{Namespace: namespace}
310 }
311
312 /*
313  * Helpers
314  */
315 func (m *Metrics) getFullName(opts prometheus.Opts, labels []string) string {
316         labelname := ""
317         for _, lbl := range labels {
318                 if len(labelname) == 0 {
319                         labelname += lbl
320                 } else {
321                         labelname += "_" + lbl
322                 }
323         }
324         return fmt.Sprintf("%s_%s_%s_%s", opts.Namespace, opts.Subsystem, opts.Name, labelname)
325 }
326
327 //
328 //
329 //
330 func (m *Metrics) RegisterCounter(opts CounterOpts, subsytem string) Counter {
331         globalLock.Lock()
332         defer globalLock.Unlock()
333         opts.Namespace = m.Namespace
334         opts.Subsystem = subsytem
335         id := m.getFullName(prometheus.Opts(opts), []string{})
336         if _, ok := cache_allcountervects[id]; ok {
337                 Logger.Warn("Register new counter with opts: %v, name conflicts existing counter vector", opts)
338                 return nil
339         }
340         if _, ok := cache_allcounters[id]; !ok {
341                 Logger.Debug("Register new counter with opts: %v", opts)
342                 cache_allcounters[id] = promauto.NewCounter(prometheus.CounterOpts(opts))
343         }
344         return cache_allcounters[id]
345 }
346
347 //
348 //
349 //
350 func (m *Metrics) RegisterCounterGroup(optsgroup []CounterOpts, subsytem string) map[string]Counter {
351         c := make(map[string]Counter)
352         for _, opts := range optsgroup {
353                 c[opts.Name] = m.RegisterCounter(opts, subsytem)
354         }
355         return c
356 }
357
358 //
359 //
360 //
361 func (m *Metrics) RegisterLabeledCounter(opts CounterOpts, labelNames []string, labelValues []string, subsytem string) Counter {
362         globalLock.Lock()
363         defer globalLock.Unlock()
364         opts.Namespace = m.Namespace
365         opts.Subsystem = subsytem
366         vecid := m.getFullName(prometheus.Opts(opts), []string{})
367         if _, ok := cache_allcounters[vecid]; ok {
368                 Logger.Warn("Register new counter vector with opts: %v labelNames: %v, name conflicts existing counter", opts, labelNames)
369                 return nil
370         }
371         if _, ok := cache_allcountervects[vecid]; !ok {
372                 Logger.Debug("Register new counter vector with opts: %v labelNames: %v", opts, labelNames)
373                 entry := CounterVec{}
374                 entry.Opts = opts
375                 entry.Labels = labelNames
376                 entry.Vec = promauto.NewCounterVec(prometheus.CounterOpts(entry.Opts), entry.Labels)
377                 cache_allcountervects[vecid] = entry
378         }
379         entry := cache_allcountervects[vecid]
380         if strSliceCompare(entry.Labels, labelNames) == false {
381                 Logger.Warn("id:%s cached counter vec labels dont match %v != %v", vecid, entry.Labels, labelNames)
382                 return nil
383         }
384         valid := m.getFullName(prometheus.Opts(entry.Opts), labelValues)
385         if _, ok := cache_allcounters[valid]; !ok {
386                 Logger.Debug("Register new counter from vector with opts: %v labelValues: %v", entry.Opts, labelValues)
387                 cache_allcounters[valid] = entry.Vec.WithLabelValues(labelValues...)
388         }
389         return cache_allcounters[valid]
390 }
391
392 //
393 //
394 //
395 func (m *Metrics) RegisterLabeledCounterGroup(optsgroup []CounterOpts, labelNames []string, labelValues []string, subsytem string) map[string]Counter {
396         c := make(map[string]Counter)
397         for _, opts := range optsgroup {
398                 c[opts.Name] = m.RegisterLabeledCounter(opts, labelNames, labelValues, subsytem)
399         }
400         return c
401 }
402
403 //
404 //
405 //
406 func (m *Metrics) RegisterGauge(opts CounterOpts, subsytem string) Gauge {
407         globalLock.Lock()
408         defer globalLock.Unlock()
409         opts.Namespace = m.Namespace
410         opts.Subsystem = subsytem
411         id := m.getFullName(prometheus.Opts(opts), []string{})
412         if _, ok := cache_allgaugevects[id]; ok {
413                 Logger.Warn("Register new gauge with opts: %v, name conflicts existing gauge vector", opts)
414                 return nil
415         }
416         if _, ok := cache_allgauges[id]; !ok {
417                 Logger.Debug("Register new gauge with opts: %v", opts)
418                 cache_allgauges[id] = promauto.NewGauge(prometheus.GaugeOpts(opts))
419         }
420         return cache_allgauges[id]
421 }
422
423 //
424 //
425 //
426 func (m *Metrics) RegisterGaugeGroup(optsgroup []CounterOpts, subsytem string) map[string]Gauge {
427         c := make(map[string]Gauge)
428         for _, opts := range optsgroup {
429                 c[opts.Name] = m.RegisterGauge(opts, subsytem)
430         }
431         return c
432 }
433
434 //
435 //
436 //
437 func (m *Metrics) RegisterLabeledGauge(opts CounterOpts, labelNames []string, labelValues []string, subsytem string) Gauge {
438         globalLock.Lock()
439         defer globalLock.Unlock()
440         opts.Namespace = m.Namespace
441         opts.Subsystem = subsytem
442         vecid := m.getFullName(prometheus.Opts(opts), []string{})
443         if _, ok := cache_allgauges[vecid]; ok {
444                 Logger.Warn("Register new gauge vector with opts: %v labelNames: %v, name conflicts existing counter", opts, labelNames)
445                 return nil
446         }
447         if _, ok := cache_allgaugevects[vecid]; !ok {
448                 Logger.Debug("Register new gauge vector with opts: %v labelNames: %v", opts, labelNames)
449                 entry := GaugeVec{}
450                 entry.Opts = opts
451                 entry.Labels = labelNames
452                 entry.Vec = promauto.NewGaugeVec(prometheus.GaugeOpts(entry.Opts), entry.Labels)
453                 cache_allgaugevects[vecid] = entry
454         }
455         entry := cache_allgaugevects[vecid]
456         if strSliceCompare(entry.Labels, labelNames) == false {
457                 Logger.Warn("id:%s cached gauge vec labels dont match %v != %v", vecid, entry.Labels, labelNames)
458                 return nil
459         }
460         valid := m.getFullName(prometheus.Opts(entry.Opts), labelValues)
461         if _, ok := cache_allgauges[valid]; !ok {
462                 Logger.Debug("Register new gauge from vector with opts: %v labelValues: %v", entry.Opts, labelValues)
463                 cache_allgauges[valid] = entry.Vec.WithLabelValues(labelValues...)
464         }
465         return cache_allgauges[valid]
466 }
467
468 //
469 //
470 //
471 func (m *Metrics) RegisterLabeledGaugeGroup(optsgroup []CounterOpts, labelNames []string, labelValues []string, subsytem string) map[string]Gauge {
472         c := make(map[string]Gauge)
473         for _, opts := range optsgroup {
474                 c[opts.Name] = m.RegisterLabeledGauge(opts, labelNames, labelValues, subsytem)
475         }
476         return c
477 }