Skip to content

Commit

Permalink
Revert "Revert "Kanister Prometheus metrics skeleton framework (#2163… (
Browse files Browse the repository at this point in the history
#2265)

* Revert "Revert "Kanister Prometheus metrics skeleton framework (#2163)" (#2262)"

This reverts commit dd714ba.

* Added _ import for metrics pkg
  • Loading branch information
mellon-collie committed Aug 11, 2023
1 parent 9023a30 commit c1e4639
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 6 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
github.com/vmware/govmomi v0.30.7
go.uber.org/zap v1.25.0
golang.org/x/oauth2 v0.11.0
gonum.org/v1/gonum v0.13.0
google.golang.org/api v0.135.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
Expand Down Expand Up @@ -169,15 +170,15 @@ require (
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc=
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -710,8 +710,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand All @@ -720,6 +720,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM=
gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
"github.com/kanisterio/kanister/pkg/eventer"
"github.com/kanisterio/kanister/pkg/field"
"github.com/kanisterio/kanister/pkg/log"
_ "github.com/kanisterio/kanister/pkg/metrics"
"github.com/kanisterio/kanister/pkg/param"
"github.com/kanisterio/kanister/pkg/progress"
"github.com/kanisterio/kanister/pkg/reconcile"
Expand Down
251 changes: 251 additions & 0 deletions pkg/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// Copyright 2023 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metrics

import (
"errors"
"fmt"

"github.com/prometheus/client_golang/prometheus"
"gonum.org/v1/gonum/stat/combin"

"github.com/kanisterio/kanister/pkg/log"
)

// BoundedLabel is a type that represents a label and its associated valid values.
type BoundedLabel struct {
LabelName string
LabelValues []string
}

// getLabelNames gets the all the `LabelName` fields from each BoundedLabel struct.
func getLabelNames(bl []BoundedLabel) []string {
ln := make([]string, len(bl))
for idx, l := range bl {
ln[idx] = l.LabelName
}
return ln
}

// verifyBoundedLabels checks if the BoundedLabel list is valid
// returns true if valid, and false if invalid.
func verifyBoundedLabels(bl []BoundedLabel) bool {
if len(bl) == 0 {
return false
}
for _, l := range bl {
if len(l.LabelValues) == 0 {
return false
}
}
return true
}

// getLabelCombinations takes a slice of BoundedLabel elements and
// returns a list of permutations of possible label permutations.
func getLabelCombinations(bl []BoundedLabel) ([]prometheus.Labels, error) {
/*
Considering the following example - If we have two BoundedLabel elements:
BoundedLabel{
LabelName: "operation_type"
LabelValues: ["backup", "restore"]
}
BoundedLabel{
LabelName: "action_set_resolution"
LabelValues: ["success", "failure"]
}
The following code generates the permutation list:
[ {"operation_type": "backup", "action_set_resolution": "success"},
{"operation_type": "backup", "action_set_resolution": "failure"},
{"operation_type": "restore", "action_set_resolution": "success"},
{"operation_type": "restore", "action_set_resolution": "failure"}]
*/
if !verifyBoundedLabels(bl) {
return nil, errors.New("invalid BoundedLabel list")
}
labelLens := make([]int, len(bl))
for idx, l := range bl {
labelLens[idx] = len(l.LabelValues)
}
idxPermutations := combin.Cartesian(labelLens)

resultPrometheusLabels := make([]prometheus.Labels, len(idxPermutations))

// generate the actual label permutations from the index permutations
// obtained
for idx, perm := range idxPermutations {
labelSet := make(prometheus.Labels)
for idx, p := range perm {
labelSet[bl[idx].LabelName] = bl[idx].LabelValues[p]
}
resultPrometheusLabels[idx] = labelSet
}
return resultPrometheusLabels, nil
}

// setDefaultCounterWithLabels initializes all the counters within a CounterVec
// and sets them to 0.
func setDefaultCounterWithLabels(cv *prometheus.CounterVec, l []prometheus.Labels) {
for _, c := range l {
cv.With(c)
}
}

// InitCounterVec initializes and registers the counter metrics vector. It
// takes a list of BoundedLabel objects - if any label value or label name is
// nil, then this method will panic. Based on the combinations returned by
// generateCombinations, it will set each counter value to 0.
// If a nil counter is returned during the registration, the method will panic.
func InitCounterVec(r prometheus.Registerer, opts prometheus.CounterOpts, bl []BoundedLabel) *prometheus.CounterVec {
labels := getLabelNames(bl)
v := prometheus.NewCounterVec(opts, labels)
gv := registerCounterVec(r, v)
combinations, err := getLabelCombinations(bl)
if err != nil {
panic(fmt.Sprintf("failed to register CounterVec. error: %v", err))
}
setDefaultCounterWithLabels(gv, combinations)
return gv
}

// InitGaugeVec initializes the gauge metrics vector. It takes a list of
// BoundedLabels, but the LabelValue field of each BoundedLabel will be
// ignored. The method panics if there are any errors (except for
// AlreadyRegisteredError) during the registration of the metric.
func InitGaugeVec(r prometheus.Registerer, opts prometheus.GaugeOpts, bl []BoundedLabel) *prometheus.GaugeVec {
labels := getLabelNames(bl)
v := prometheus.NewGaugeVec(opts, labels)
gv := registerGaugeVec(r, v)
return gv
}

// InitHistogramVec initializes the histogram metrics vector. It takes a list
// of BoundedLabels, but the LabelValue field of each BoundedLabel will be
// ignored. The method panics if there are any errors (except for
// AlreadyRegisteredError) during the registration of the metric.
func InitHistogramVec(r prometheus.Registerer, opts prometheus.HistogramOpts, bl []BoundedLabel) *prometheus.HistogramVec {
labels := getLabelNames(bl)
v := prometheus.NewHistogramVec(opts, labels)
h := registerHistogramVec(r, v)
return h
}

// InitCounter initializes a new counter. The method panics if there are any
// errors (except for AlreadyRegisteredError) during the registration of the
// metric.
func InitCounter(r prometheus.Registerer, opts prometheus.CounterOpts) prometheus.Counter {
c := prometheus.NewCounter(opts)
rc := registerCounter(r, c)
return rc
}

// InitGauge initializes a new gauge metric. The method panics if there are any
// errors (except for AlreadyRegisteredError) during the registration of the
// metric.
func InitGauge(r prometheus.Registerer, opts prometheus.GaugeOpts) prometheus.Gauge {
g := prometheus.NewGauge(opts)
rg := registerGauge(r, g)
return rg
}

// InitHistogram initializes a new histogram metric. The method panics if there
// are any errors (except for AlreadyRegisteredError) during the registration
// of the metric.
func InitHistogram(r prometheus.Registerer, opts prometheus.HistogramOpts) prometheus.Histogram {
h := prometheus.NewHistogram(opts)
rh := registerHistogram(r, h)
return rh
}

// registerCounterVec registers the CounterVec with the provided Registerer. It
// panics if the type check fails.
func registerCounterVec(r prometheus.Registerer, g *prometheus.CounterVec) *prometheus.CounterVec {
c := registerMetricOrDie(r, g)
gv, ok := c.(*prometheus.CounterVec)
if !ok {
panic("failed type check for CounterVec")
}
return gv
}

// registerHistogramVec registers the HistogramVec with the provided
// Registerer. It panics if the type check fails.
func registerHistogramVec(r prometheus.Registerer, h *prometheus.HistogramVec) *prometheus.HistogramVec {
c := registerMetricOrDie(r, h)
v, ok := c.(*prometheus.HistogramVec)
if !ok {
panic("failed type check for HistogramVec")
}
return v
}

// registerGaugeVec registers the GaugeVec with the provided Registerer. It
// panics if the type check fails.
func registerGaugeVec(r prometheus.Registerer, g *prometheus.GaugeVec) *prometheus.GaugeVec {
c := registerMetricOrDie(r, g)
gv, ok := c.(*prometheus.GaugeVec)
if !ok {
panic("failed type check for GaugeVec")
}
return gv
}

// registerCounter registers the Counter with the provided Registerer. It
// panics if the type check fails.
func registerCounter(r prometheus.Registerer, ctr prometheus.Counter) prometheus.Counter {
c := registerMetricOrDie(r, ctr)
rg, ok := c.(prometheus.Counter)
if !ok {
panic("failed type check for Counter")
}
return rg
}

// registerHistogram registers the Histogram with the provided Registerer. It
// panics if the type check fails.
func registerHistogram(r prometheus.Registerer, g prometheus.Histogram) prometheus.Histogram {
c := registerMetricOrDie(r, g)
rg, ok := c.(prometheus.Histogram)
if !ok {
panic("failed type check for Histogram")
}
return rg
}

// registerGauge registers the Gauge with the provided Registerer. It panics if
// the type check fails.
func registerGauge(r prometheus.Registerer, g prometheus.Gauge) prometheus.Gauge {
c := registerMetricOrDie(r, g)
rg, ok := c.(prometheus.Gauge)
if !ok {
panic("failed type check for Gauge")
}
return rg
}

// registerMetricOrDie is a helper to register a metric and log registration
// errors. If the metric already exists, then it will be logged, and the metric
// is returned. For other errors, the method panics.
func registerMetricOrDie(r prometheus.Registerer, c prometheus.Collector) prometheus.Collector {
if err := r.Register(c); err != nil {
are := prometheus.AlreadyRegisteredError{}
if !errors.As(err, &are) {
panic(fmt.Sprintf("failed to register metric. error: %v", err))
}
// Use already registered metric
log.Debug().Print("Metric already registered")
return are.ExistingCollector
}
return c
}
Loading

0 comments on commit c1e4639

Please sign in to comment.