2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
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
10 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
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"
32 //-----------------------------------------------------------------------------
34 //-----------------------------------------------------------------------------
35 func strSliceCompare(a, b []string) bool {
47 //-----------------------------------------------------------------------------
49 //-----------------------------------------------------------------------------
50 type CounterOpts prometheus.Opts
51 type Counter prometheus.Counter
52 type Gauge prometheus.Gauge
54 type CounterVec struct {
55 Vec *prometheus.CounterVec
60 type GaugeVec struct {
61 Vec *prometheus.GaugeVec
66 //-----------------------------------------------------------------------------
68 //-----------------------------------------------------------------------------
69 type MetricGroupsCacheCounterRegisterer interface {
70 RegisterCounter(CounterOpts) Counter
73 type MetricGroupsCacheCounterRegistererFunc func(CounterOpts) Counter
75 func (fn MetricGroupsCacheCounterRegistererFunc) RegisterCounter(copts CounterOpts) Counter {
79 //-----------------------------------------------------------------------------
81 //-----------------------------------------------------------------------------
82 type MetricGroupsCacheGaugeRegisterer interface {
83 RegisterGauge(CounterOpts) Gauge
86 type MetricGroupsCacheGaugeRegistererFunc func(CounterOpts) Gauge
88 func (fn MetricGroupsCacheGaugeRegistererFunc) RegisterGauge(copts CounterOpts) Gauge {
92 //-----------------------------------------------------------------------------
94 //-----------------------------------------------------------------------------
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
104 func (met *MetricGroupsCache) Registerer(regcnt MetricGroupsCacheCounterRegisterer, reggau MetricGroupsCacheGaugeRegisterer) {
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
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
126 func (met *MetricGroupsCache) CIs(metric string) bool {
129 _, ok := met.counters[metric]
133 func (met *MetricGroupsCache) CGet(metric string) Counter {
136 cntr, ok := met.counters[metric]
138 cntr = met.cReg(metric)
143 func (met *MetricGroupsCache) CInc(metric string) {
146 cntr, ok := met.counters[metric]
148 cntr = met.cReg(metric)
153 func (met *MetricGroupsCache) CAdd(metric string, val float64) {
156 cntr, ok := met.counters[metric]
158 cntr = met.cReg(metric)
163 func (met *MetricGroupsCache) GIs(metric string) bool {
166 _, ok := met.gauges[metric]
170 func (met *MetricGroupsCache) GGet(metric string) Gauge {
173 gaug, ok := met.gauges[metric]
175 gaug = met.gReg(metric)
180 func (met *MetricGroupsCache) GSet(metric string, val float64) {
183 gaug, ok := met.gauges[metric]
185 gaug = met.gReg(metric)
190 func (met *MetricGroupsCache) GAdd(metric string, val float64) {
193 gaug, ok := met.gauges[metric]
195 gaug = met.gReg(metric)
200 func (met *MetricGroupsCache) GInc(metric string) {
203 gaug, ok := met.gauges[metric]
205 gaug = met.gReg(metric)
210 func (met *MetricGroupsCache) GDec(metric string) {
213 gaug, ok := met.gauges[metric]
215 gaug = met.gReg(metric)
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
228 func (met *MetricGroupsCache) CombineCounterGroupsWithPrefix(prefix string, srcs ...map[string]Counter) {
231 met.combineCounterGroupsWithPrefix(prefix, srcs...)
234 func (met *MetricGroupsCache) CombineCounterGroups(srcs ...map[string]Counter) {
237 met.combineCounterGroupsWithPrefix("", srcs...)
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
248 func (met *MetricGroupsCache) CombineGaugeGroupsWithPrefix(prefix string, srcs ...map[string]Gauge) {
251 met.combineGaugeGroupsWithPrefix(prefix, srcs...)
254 func (met *MetricGroupsCache) CombineGaugeGroups(srcs ...map[string]Gauge) {
257 met.combineGaugeGroupsWithPrefix("", srcs...)
260 func NewMetricGroupsCache() *MetricGroupsCache {
261 entry := &MetricGroupsCache{}
262 entry.counters = make(map[string]Counter)
263 entry.gauges = make(map[string]Gauge)
269 func NewMetricGroupsCacheWithRegisterers(regcnt MetricGroupsCacheCounterRegisterer, reggau MetricGroupsCacheGaugeRegisterer) *MetricGroupsCache {
270 entry := NewMetricGroupsCache()
271 entry.regcnt = regcnt
272 entry.reggau = reggau
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
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)
293 //-----------------------------------------------------------------------------
295 //-----------------------------------------------------------------------------
296 type Metrics struct {
300 func NewMetrics(url, namespace string, r *mux.Router) *Metrics {
302 url = "/ric/v1/metrics"
304 Logger.Info("Serving metrics on: url=%s namespace=%s", url, namespace)
306 // Expose 'metrics' endpoint with standard golang metrics used by prometheus
307 r.Handle(url, promhttp.Handler())
309 return &Metrics{Namespace: namespace}
315 func (m *Metrics) getFullName(opts prometheus.Opts, labels []string) string {
317 for _, lbl := range labels {
318 if len(labelname) == 0 {
321 labelname += "_" + lbl
324 return fmt.Sprintf("%s_%s_%s_%s", opts.Namespace, opts.Subsystem, opts.Name, labelname)
330 func (m *Metrics) RegisterCounter(opts CounterOpts, subsytem string) Counter {
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)
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))
344 return cache_allcounters[id]
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)
361 func (m *Metrics) RegisterLabeledCounter(opts CounterOpts, labelNames []string, labelValues []string, subsytem string) Counter {
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)
371 if _, ok := cache_allcountervects[vecid]; !ok {
372 Logger.Debug("Register new counter vector with opts: %v labelNames: %v", opts, labelNames)
373 entry := CounterVec{}
375 entry.Labels = labelNames
376 entry.Vec = promauto.NewCounterVec(prometheus.CounterOpts(entry.Opts), entry.Labels)
377 cache_allcountervects[vecid] = entry
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)
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...)
389 return cache_allcounters[valid]
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)
406 func (m *Metrics) RegisterGauge(opts CounterOpts, subsytem string) Gauge {
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)
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))
420 return cache_allgauges[id]
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)
437 func (m *Metrics) RegisterLabeledGauge(opts CounterOpts, labelNames []string, labelValues []string, subsytem string) Gauge {
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)
447 if _, ok := cache_allgaugevects[vecid]; !ok {
448 Logger.Debug("Register new gauge vector with opts: %v labelNames: %v", opts, labelNames)
451 entry.Labels = labelNames
452 entry.Vec = promauto.NewGaugeVec(prometheus.GaugeOpts(entry.Opts), entry.Labels)
453 cache_allgaugevects[vecid] = entry
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)
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...)
465 return cache_allgauges[valid]
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)