mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #77618 from logicalhan/other-metric-wrappers
add wrappers around gauge, histogram & summary
This commit is contained in:
commit
33d763ff3a
@ -10,9 +10,12 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"counter.go",
|
||||
"gauge.go",
|
||||
"histogram.go",
|
||||
"metric.go",
|
||||
"opts.go",
|
||||
"registry.go",
|
||||
"summary.go",
|
||||
"version_parser.go",
|
||||
"wrappers.go",
|
||||
],
|
||||
@ -31,7 +34,10 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"counter_test.go",
|
||||
"gauge_test.go",
|
||||
"histogram_test.go",
|
||||
"registry_test.go",
|
||||
"summary_test.go",
|
||||
"version_parser_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
|
150
staging/src/k8s.io/component-base/metrics/gauge.go
Normal file
150
staging/src/k8s.io/component-base/metrics/gauge.go
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Gauge is our internal representation for our wrapping struct around prometheus
|
||||
// gauges. kubeGauge implements both KubeCollector and KubeGauge.
|
||||
type Gauge struct {
|
||||
GaugeMetric
|
||||
*GaugeOpts
|
||||
lazyMetric
|
||||
selfCollector
|
||||
}
|
||||
|
||||
// NewGauge returns an object which satisfies the KubeCollector and KubeGauge interfaces.
|
||||
// However, the object returned will not measure anything unless the collector is first
|
||||
// registered, since the metric is lazily instantiated.
|
||||
func NewGauge(opts *GaugeOpts) *Gauge {
|
||||
// todo: handle defaulting better
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
kc := &Gauge{
|
||||
GaugeOpts: opts,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
kc.setPrometheusGauge(noop)
|
||||
kc.lazyInit(kc)
|
||||
return kc
|
||||
}
|
||||
|
||||
// setPrometheusGauge sets the underlying KubeGauge object, i.e. the thing that does the measurement.
|
||||
func (g *Gauge) setPrometheusGauge(gauge prometheus.Gauge) {
|
||||
g.GaugeMetric = gauge
|
||||
g.initSelfCollection(gauge)
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (g *Gauge) DeprecatedVersion() *semver.Version {
|
||||
return g.GaugeOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
// initializeMetric invocation creates the actual underlying Gauge. Until this method is called
|
||||
// the underlying gauge is a no-op.
|
||||
func (g *Gauge) initializeMetric() {
|
||||
g.GaugeOpts.annotateStabilityLevel()
|
||||
// this actually creates the underlying prometheus gauge.
|
||||
g.setPrometheusGauge(prometheus.NewGauge(g.GaugeOpts.toPromGaugeOpts()))
|
||||
}
|
||||
|
||||
// initializeDeprecatedMetric invocation creates the actual (but deprecated) Gauge. Until this method
|
||||
// is called the underlying gauge is a no-op.
|
||||
func (g *Gauge) initializeDeprecatedMetric() {
|
||||
g.GaugeOpts.markDeprecated()
|
||||
g.initializeMetric()
|
||||
}
|
||||
|
||||
// GaugeVec is the internal representation of our wrapping struct around prometheus
|
||||
// gaugeVecs. kubeGaugeVec implements both KubeCollector and KubeGaugeVec.
|
||||
type GaugeVec struct {
|
||||
*prometheus.GaugeVec
|
||||
*GaugeOpts
|
||||
lazyMetric
|
||||
originalLabels []string
|
||||
}
|
||||
|
||||
// NewGaugeVec returns an object which satisfies the KubeCollector and KubeGaugeVec interfaces.
|
||||
// However, the object returned will not measure anything unless the collector is first
|
||||
// registered, since the metric is lazily instantiated.
|
||||
func NewGaugeVec(opts *GaugeOpts, labels []string) *GaugeVec {
|
||||
// todo: handle defaulting better
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
cv := &GaugeVec{
|
||||
GaugeVec: noopGaugeVec,
|
||||
GaugeOpts: opts,
|
||||
originalLabels: labels,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
cv.lazyInit(cv)
|
||||
return cv
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (v *GaugeVec) DeprecatedVersion() *semver.Version {
|
||||
return v.GaugeOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
// initializeMetric invocation creates the actual underlying GaugeVec. Until this method is called
|
||||
// the underlying gaugeVec is a no-op.
|
||||
func (v *GaugeVec) initializeMetric() {
|
||||
v.GaugeOpts.annotateStabilityLevel()
|
||||
v.GaugeVec = prometheus.NewGaugeVec(v.GaugeOpts.toPromGaugeOpts(), v.originalLabels)
|
||||
}
|
||||
|
||||
// initializeDeprecatedMetric invocation creates the actual (but deprecated) GaugeVec. Until this method is called
|
||||
// the underlying gaugeVec is a no-op.
|
||||
func (v *GaugeVec) initializeDeprecatedMetric() {
|
||||
v.GaugeOpts.markDeprecated()
|
||||
v.initializeMetric()
|
||||
}
|
||||
|
||||
// Default Prometheus behavior actually results in the creation of a new metric
|
||||
// if a metric with the unique label values is not found in the underlying stored metricMap.
|
||||
// This means that if this function is called but the underlying metric is not registered
|
||||
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
|
||||
// for perpetuity (i.e. throughout application lifecycle).
|
||||
//
|
||||
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/gauge.go#L190-L208
|
||||
|
||||
// WithLabelValues returns the GaugeMetric for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new GaugeMetric is created IFF the gaugeVec
|
||||
// has been registered to a metrics registry.
|
||||
func (v *GaugeVec) WithLabelValues(lvs ...string) GaugeMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop // return no-op gauge
|
||||
}
|
||||
return v.GaugeVec.WithLabelValues(lvs...)
|
||||
}
|
||||
|
||||
// With returns the GaugeMetric for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new GaugeMetric is created IFF the gaugeVec has
|
||||
// been registered to a metrics registry.
|
||||
func (v *GaugeVec) With(labels prometheus.Labels) GaugeMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop // return no-op gauge
|
||||
}
|
||||
return v.GaugeVec.With(labels)
|
||||
}
|
210
staging/src/k8s.io/component-base/metrics/gauge_test.go
Normal file
210
staging/src/k8s.io/component-base/metrics/gauge_test.go
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGauge(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
GaugeOpts
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] gauge help",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
DeprecatedVersion: &v115,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) gauge help",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
DeprecatedVersion: &v114,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "gauge help",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewGauge(&test.GaugeOpts)
|
||||
registry.MustRegister(c)
|
||||
|
||||
ms, err := registry.Gather()
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.Set(100)
|
||||
c.Set(101)
|
||||
expected := 101
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
for _, m := range mf.GetMetric() {
|
||||
if int(m.GetGauge().GetValue()) != expected {
|
||||
t.Errorf("Got %v, wanted %v as the count", m.GetGauge().GetValue(), expected)
|
||||
}
|
||||
t.Logf("%v\n", m.GetGauge().GetValue())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaugeVec(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
GaugeOpts
|
||||
labels []string
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] gauge help",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
DeprecatedVersion: &v115,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) gauge help",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
GaugeOpts: GaugeOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "gauge help",
|
||||
DeprecatedVersion: &v114,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "gauge help",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewGaugeVec(&test.GaugeOpts, test.labels)
|
||||
registry.MustRegister(c)
|
||||
c.WithLabelValues("1", "2").Set(1.0)
|
||||
ms, err := registry.Gather()
|
||||
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.WithLabelValues("1", "3").Set(1.0)
|
||||
c.WithLabelValues("2", "3").Set(1.0)
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
if len(mf.GetMetric()) != 3 {
|
||||
t.Errorf("Got %v metrics, wanted 2 as the count", len(mf.GetMetric()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
148
staging/src/k8s.io/component-base/metrics/histogram.go
Normal file
148
staging/src/k8s.io/component-base/metrics/histogram.go
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// Histogram is our internal representation for our wrapping struct around prometheus
|
||||
// histograms. Summary implements both KubeCollector and ObserverMetric
|
||||
type Histogram struct {
|
||||
ObserverMetric
|
||||
*HistogramOpts
|
||||
lazyMetric
|
||||
selfCollector
|
||||
}
|
||||
|
||||
// NewHistogram returns an object which is Histogram-like. However, nothing
|
||||
// will be measured until the histogram is registered somewhere.
|
||||
func NewHistogram(opts *HistogramOpts) *Histogram {
|
||||
// todo: handle defaulting better
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
h := &Histogram{
|
||||
HistogramOpts: opts,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
h.setPrometheusHistogram(noopMetric{})
|
||||
h.lazyInit(h)
|
||||
return h
|
||||
}
|
||||
|
||||
// setPrometheusHistogram sets the underlying KubeGauge object, i.e. the thing that does the measurement.
|
||||
func (h *Histogram) setPrometheusHistogram(histogram prometheus.Histogram) {
|
||||
h.ObserverMetric = histogram
|
||||
h.initSelfCollection(histogram)
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (h *Histogram) DeprecatedVersion() *semver.Version {
|
||||
return h.HistogramOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
// initializeMetric invokes the actual prometheus.Histogram object instantiation
|
||||
// and stores a reference to it
|
||||
func (h *Histogram) initializeMetric() {
|
||||
h.HistogramOpts.annotateStabilityLevel()
|
||||
// this actually creates the underlying prometheus gauge.
|
||||
h.setPrometheusHistogram(prometheus.NewHistogram(h.HistogramOpts.toPromHistogramOpts()))
|
||||
}
|
||||
|
||||
// initializeDeprecatedMetric invokes the actual prometheus.Histogram object instantiation
|
||||
// but modifies the Help description prior to object instantiation.
|
||||
func (h *Histogram) initializeDeprecatedMetric() {
|
||||
h.HistogramOpts.markDeprecated()
|
||||
h.initializeMetric()
|
||||
}
|
||||
|
||||
// HistogramVec is the internal representation of our wrapping struct around prometheus
|
||||
// histogramVecs.
|
||||
type HistogramVec struct {
|
||||
*prometheus.HistogramVec
|
||||
*HistogramOpts
|
||||
lazyMetric
|
||||
originalLabels []string
|
||||
}
|
||||
|
||||
// NewHistogramVec returns an object which satisfies KubeCollector and wraps the
|
||||
// prometheus.HistogramVec object. However, the object returned will not measure
|
||||
// anything unless the collector is first registered, since the metric is lazily instantiated.
|
||||
func NewHistogramVec(opts *HistogramOpts, labels []string) *HistogramVec {
|
||||
// todo: handle defaulting better
|
||||
klog.Errorf("---%v---\n", opts)
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
klog.Errorf("---%v---\n", opts)
|
||||
v := &HistogramVec{
|
||||
HistogramVec: noopHistogramVec,
|
||||
HistogramOpts: opts,
|
||||
originalLabels: labels,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
v.lazyInit(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (v *HistogramVec) DeprecatedVersion() *semver.Version {
|
||||
return v.HistogramOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
func (v *HistogramVec) initializeMetric() {
|
||||
v.HistogramOpts.annotateStabilityLevel()
|
||||
v.HistogramVec = prometheus.NewHistogramVec(v.HistogramOpts.toPromHistogramOpts(), v.originalLabels)
|
||||
}
|
||||
|
||||
func (v *HistogramVec) initializeDeprecatedMetric() {
|
||||
v.HistogramOpts.markDeprecated()
|
||||
v.initializeMetric()
|
||||
}
|
||||
|
||||
// Default Prometheus behavior actually results in the creation of a new metric
|
||||
// if a metric with the unique label values is not found in the underlying stored metricMap.
|
||||
// This means that if this function is called but the underlying metric is not registered
|
||||
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
|
||||
// for perpetuity (i.e. throughout application lifecycle).
|
||||
//
|
||||
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/histogram.go#L460-L470
|
||||
|
||||
// WithLabelValues returns the ObserverMetric for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new ObserverMetric is created IFF the HistogramVec
|
||||
// has been registered to a metrics registry.
|
||||
func (v *HistogramVec) WithLabelValues(lvs ...string) ObserverMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop
|
||||
}
|
||||
return v.HistogramVec.WithLabelValues(lvs...)
|
||||
}
|
||||
|
||||
// With returns the ObserverMetric for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new ObserverMetric is created IFF the HistogramVec has
|
||||
// been registered to a metrics registry.
|
||||
func (v *HistogramVec) With(labels prometheus.Labels) ObserverMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop
|
||||
}
|
||||
return v.HistogramVec.With(labels)
|
||||
}
|
225
staging/src/k8s.io/component-base/metrics/histogram_test.go
Normal file
225
staging/src/k8s.io/component-base/metrics/histogram_test.go
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHistogram(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
HistogramOpts
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] histogram help message",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
DeprecatedVersion: &v115,
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) histogram help message",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
DeprecatedVersion: &v114,
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "histogram help message",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewHistogram(&test.HistogramOpts)
|
||||
registry.MustRegister(c)
|
||||
|
||||
ms, err := registry.Gather()
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.Observe(1)
|
||||
c.Observe(2)
|
||||
c.Observe(3)
|
||||
c.Observe(1.5)
|
||||
expected := 4
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
for _, m := range mf.GetMetric() {
|
||||
if int(m.GetHistogram().GetSampleCount()) != expected {
|
||||
t.Errorf("Got %v, want %v as the sample count", m.GetHistogram().GetSampleCount(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistogramVec(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
HistogramOpts
|
||||
labels []string
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] histogram help message",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
DeprecatedVersion: &v115,
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) histogram help message",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
HistogramOpts: HistogramOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "histogram help message",
|
||||
DeprecatedVersion: &v114,
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "histogram help message",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewHistogramVec(&test.HistogramOpts, test.labels)
|
||||
registry.MustRegister(c)
|
||||
c.WithLabelValues("1", "2").Observe(1.0)
|
||||
ms, err := registry.Gather()
|
||||
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.WithLabelValues("1", "3").Observe(1.0)
|
||||
c.WithLabelValues("2", "3").Observe(1.0)
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
if len(mf.GetMetric()) != 3 {
|
||||
t.Errorf("Got %v metrics, wanted 2 as the count", len(mf.GetMetric()))
|
||||
}
|
||||
for _, m := range mf.GetMetric() {
|
||||
if m.GetHistogram().GetSampleCount() != 1 {
|
||||
t.Errorf(
|
||||
"Got %v metrics, expected histogram sample count to equal 1",
|
||||
m.GetHistogram().GetSampleCount())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// KubeOpts is superset struct for prometheus.Opts. The prometheus Opts structure
|
||||
@ -82,3 +83,130 @@ func (o *CounterOpts) toPromCounterOpts() prometheus.CounterOpts {
|
||||
ConstLabels: o.ConstLabels,
|
||||
}
|
||||
}
|
||||
|
||||
// GaugeOpts is an alias for Opts. See there for doc comments.
|
||||
type GaugeOpts KubeOpts
|
||||
|
||||
// Modify help description on the metric description.
|
||||
func (o *GaugeOpts) markDeprecated() {
|
||||
o.deprecateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// annotateStabilityLevel annotates help description on the metric description with the stability level
|
||||
// of the metric
|
||||
func (o *GaugeOpts) annotateStabilityLevel() {
|
||||
o.annotateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// convenience function to allow easy transformation to the prometheus
|
||||
// counterpart. This will do more once we have a proper label abstraction
|
||||
func (o GaugeOpts) toPromGaugeOpts() prometheus.GaugeOpts {
|
||||
return prometheus.GaugeOpts{
|
||||
Namespace: o.Namespace,
|
||||
Subsystem: o.Subsystem,
|
||||
Name: o.Name,
|
||||
Help: o.Help,
|
||||
ConstLabels: o.ConstLabels,
|
||||
}
|
||||
}
|
||||
|
||||
// HistogramOpts bundles the options for creating a Histogram metric. It is
|
||||
// mandatory to set Name to a non-empty string. All other fields are optional
|
||||
// and can safely be left at their zero value, although it is strongly
|
||||
// encouraged to set a Help string.
|
||||
type HistogramOpts struct {
|
||||
Namespace string
|
||||
Subsystem string
|
||||
Name string
|
||||
Help string
|
||||
ConstLabels prometheus.Labels
|
||||
Buckets []float64
|
||||
DeprecatedVersion *semver.Version
|
||||
deprecateOnce sync.Once
|
||||
annotateOnce sync.Once
|
||||
StabilityLevel StabilityLevel
|
||||
}
|
||||
|
||||
// Modify help description on the metric description.
|
||||
func (o *HistogramOpts) markDeprecated() {
|
||||
o.deprecateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// annotateStabilityLevel annotates help description on the metric description with the stability level
|
||||
// of the metric
|
||||
func (o *HistogramOpts) annotateStabilityLevel() {
|
||||
o.annotateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// convenience function to allow easy transformation to the prometheus
|
||||
// counterpart. This will do more once we have a proper label abstraction
|
||||
func (o HistogramOpts) toPromHistogramOpts() prometheus.HistogramOpts {
|
||||
return prometheus.HistogramOpts{
|
||||
Namespace: o.Namespace,
|
||||
Subsystem: o.Subsystem,
|
||||
Name: o.Name,
|
||||
Help: o.Help,
|
||||
ConstLabels: o.ConstLabels,
|
||||
Buckets: o.Buckets,
|
||||
}
|
||||
}
|
||||
|
||||
// SummaryOpts bundles the options for creating a Summary metric. It is
|
||||
// mandatory to set Name to a non-empty string. While all other fields are
|
||||
// optional and can safely be left at their zero value, it is recommended to set
|
||||
// a help string and to explicitly set the Objectives field to the desired value
|
||||
// as the default value will change in the upcoming v0.10 of the library.
|
||||
type SummaryOpts struct {
|
||||
Namespace string
|
||||
Subsystem string
|
||||
Name string
|
||||
Help string
|
||||
ConstLabels prometheus.Labels
|
||||
Objectives map[float64]float64
|
||||
MaxAge time.Duration
|
||||
AgeBuckets uint32
|
||||
BufCap uint32
|
||||
DeprecatedVersion *semver.Version
|
||||
deprecateOnce sync.Once
|
||||
annotateOnce sync.Once
|
||||
StabilityLevel StabilityLevel
|
||||
}
|
||||
|
||||
// Modify help description on the metric description.
|
||||
func (o *SummaryOpts) markDeprecated() {
|
||||
o.deprecateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("(Deprecated since %v) %v", o.DeprecatedVersion, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// annotateStabilityLevel annotates help description on the metric description with the stability level
|
||||
// of the metric
|
||||
func (o *SummaryOpts) annotateStabilityLevel() {
|
||||
o.annotateOnce.Do(func() {
|
||||
o.Help = fmt.Sprintf("[%v] %v", o.StabilityLevel, o.Help)
|
||||
})
|
||||
}
|
||||
|
||||
// convenience function to allow easy transformation to the prometheus
|
||||
// counterpart. This will do more once we have a proper label abstraction
|
||||
func (o SummaryOpts) toPromSummaryOpts() prometheus.SummaryOpts {
|
||||
return prometheus.SummaryOpts{
|
||||
Namespace: o.Namespace,
|
||||
Subsystem: o.Subsystem,
|
||||
Name: o.Name,
|
||||
Help: o.Help,
|
||||
ConstLabels: o.ConstLabels,
|
||||
Objectives: o.Objectives,
|
||||
MaxAge: o.MaxAge,
|
||||
AgeBuckets: o.AgeBuckets,
|
||||
BufCap: o.BufCap,
|
||||
}
|
||||
}
|
||||
|
152
staging/src/k8s.io/component-base/metrics/summary.go
Normal file
152
staging/src/k8s.io/component-base/metrics/summary.go
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Summary is our internal representation for our wrapping struct around prometheus
|
||||
// summaries. Summary implements both KubeCollector and ObserverMetric
|
||||
//
|
||||
// DEPRECATED: as per the metrics overhaul KEP
|
||||
type Summary struct {
|
||||
ObserverMetric
|
||||
*SummaryOpts
|
||||
lazyMetric
|
||||
selfCollector
|
||||
}
|
||||
|
||||
// NewSummary returns an object which is Summary-like. However, nothing
|
||||
// will be measured until the summary is registered somewhere.
|
||||
//
|
||||
// DEPRECATED: as per the metrics overhaul KEP
|
||||
func NewSummary(opts *SummaryOpts) *Summary {
|
||||
// todo: handle defaulting better
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
s := &Summary{
|
||||
SummaryOpts: opts,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
s.setPrometheusSummary(noopMetric{})
|
||||
s.lazyInit(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// setPrometheusSummary sets the underlying KubeGauge object, i.e. the thing that does the measurement.
|
||||
func (s *Summary) setPrometheusSummary(summary prometheus.Summary) {
|
||||
s.ObserverMetric = summary
|
||||
s.initSelfCollection(summary)
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (s *Summary) DeprecatedVersion() *semver.Version {
|
||||
return s.SummaryOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
// initializeMetric invokes the actual prometheus.Summary object instantiation
|
||||
// and stores a reference to it
|
||||
func (s *Summary) initializeMetric() {
|
||||
s.SummaryOpts.annotateStabilityLevel()
|
||||
// this actually creates the underlying prometheus gauge.
|
||||
s.setPrometheusSummary(prometheus.NewSummary(s.SummaryOpts.toPromSummaryOpts()))
|
||||
}
|
||||
|
||||
// initializeDeprecatedMetric invokes the actual prometheus.Summary object instantiation
|
||||
// but modifies the Help description prior to object instantiation.
|
||||
func (s *Summary) initializeDeprecatedMetric() {
|
||||
s.SummaryOpts.markDeprecated()
|
||||
s.initializeMetric()
|
||||
}
|
||||
|
||||
// SummaryVec is the internal representation of our wrapping struct around prometheus
|
||||
// summaryVecs.
|
||||
//
|
||||
// DEPRECATED: as per the metrics overhaul KEP
|
||||
type SummaryVec struct {
|
||||
*prometheus.SummaryVec
|
||||
*SummaryOpts
|
||||
lazyMetric
|
||||
originalLabels []string
|
||||
}
|
||||
|
||||
// NewSummaryVec returns an object which satisfies KubeCollector and wraps the
|
||||
// prometheus.SummaryVec object. However, the object returned will not measure
|
||||
// anything unless the collector is first registered, since the metric is lazily instantiated.
|
||||
//
|
||||
// DEPRECATED: as per the metrics overhaul KEP
|
||||
func NewSummaryVec(opts *SummaryOpts, labels []string) *SummaryVec {
|
||||
// todo: handle defaulting better
|
||||
if opts.StabilityLevel == "" {
|
||||
opts.StabilityLevel = ALPHA
|
||||
}
|
||||
v := &SummaryVec{
|
||||
SummaryOpts: opts,
|
||||
originalLabels: labels,
|
||||
lazyMetric: lazyMetric{},
|
||||
}
|
||||
v.lazyInit(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// DeprecatedVersion returns a pointer to the Version or nil
|
||||
func (v *SummaryVec) DeprecatedVersion() *semver.Version {
|
||||
return v.SummaryOpts.DeprecatedVersion
|
||||
}
|
||||
|
||||
func (v *SummaryVec) initializeMetric() {
|
||||
v.SummaryOpts.annotateStabilityLevel()
|
||||
v.SummaryVec = prometheus.NewSummaryVec(v.SummaryOpts.toPromSummaryOpts(), v.originalLabels)
|
||||
}
|
||||
|
||||
func (v *SummaryVec) initializeDeprecatedMetric() {
|
||||
v.SummaryOpts.markDeprecated()
|
||||
v.initializeMetric()
|
||||
}
|
||||
|
||||
// Default Prometheus behavior actually results in the creation of a new metric
|
||||
// if a metric with the unique label values is not found in the underlying stored metricMap.
|
||||
// This means that if this function is called but the underlying metric is not registered
|
||||
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
|
||||
// for perpetuity (i.e. throughout application lifecycle).
|
||||
//
|
||||
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/summary.go#L485-L495
|
||||
|
||||
// WithLabelValues returns the ObserverMetric for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new ObserverMetric is created IFF the summaryVec
|
||||
// has been registered to a metrics registry.
|
||||
func (v *SummaryVec) WithLabelValues(lvs ...string) ObserverMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop
|
||||
}
|
||||
return v.SummaryVec.WithLabelValues(lvs...)
|
||||
}
|
||||
|
||||
// With returns the ObserverMetric for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new ObserverMetric is created IFF the summaryVec has
|
||||
// been registered to a metrics registry.
|
||||
func (v *SummaryVec) With(labels prometheus.Labels) ObserverMetric {
|
||||
if !v.IsCreated() {
|
||||
return noop
|
||||
}
|
||||
return v.SummaryVec.With(labels)
|
||||
}
|
220
staging/src/k8s.io/component-base/metrics/summary_test.go
Normal file
220
staging/src/k8s.io/component-base/metrics/summary_test.go
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes 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 (
|
||||
"github.com/blang/semver"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSummary(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
SummaryOpts
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
StabilityLevel: ALPHA,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] summary help message",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
DeprecatedVersion: &v115,
|
||||
StabilityLevel: ALPHA,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) summary help message",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
DeprecatedVersion: &v114,
|
||||
},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "summary help message",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewSummary(&test.SummaryOpts)
|
||||
registry.MustRegister(c)
|
||||
|
||||
ms, err := registry.Gather()
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.Observe(1)
|
||||
c.Observe(2)
|
||||
c.Observe(3)
|
||||
c.Observe(1.5)
|
||||
expected := 4
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
for _, m := range mf.GetMetric() {
|
||||
if int(m.GetSummary().GetSampleCount()) != expected {
|
||||
t.Errorf("Got %v, want %v as the sample count", m.GetHistogram().GetSampleCount(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryVec(t *testing.T) {
|
||||
v115 := semver.MustParse("1.15.0")
|
||||
v114 := semver.MustParse("1.14.0")
|
||||
var tests = []struct {
|
||||
desc string
|
||||
SummaryOpts
|
||||
labels []string
|
||||
registryVersion *semver.Version
|
||||
expectedMetricCount int
|
||||
expectedHelp string
|
||||
}{
|
||||
{
|
||||
desc: "Test non deprecated",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] summary help message",
|
||||
},
|
||||
{
|
||||
desc: "Test deprecated",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
DeprecatedVersion: &v115,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 1,
|
||||
expectedHelp: "[ALPHA] (Deprecated since 1.15.0) summary help message",
|
||||
},
|
||||
{
|
||||
desc: "Test hidden",
|
||||
SummaryOpts: SummaryOpts{
|
||||
Namespace: "namespace",
|
||||
Name: "metric_test_name",
|
||||
Subsystem: "subsystem",
|
||||
Help: "summary help message",
|
||||
DeprecatedVersion: &v114,
|
||||
},
|
||||
labels: []string{"label_a", "label_b"},
|
||||
registryVersion: &v115,
|
||||
expectedMetricCount: 0,
|
||||
expectedHelp: "summary help message",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
registry := NewKubeRegistry(apimachineryversion.Info{
|
||||
Major: "1",
|
||||
Minor: "15",
|
||||
GitVersion: "v1.15.0-alpha-1.12345",
|
||||
})
|
||||
c := NewSummaryVec(&test.SummaryOpts, test.labels)
|
||||
registry.MustRegister(c)
|
||||
c.WithLabelValues("1", "2").Observe(1.0)
|
||||
ms, err := registry.Gather()
|
||||
|
||||
if len(ms) != test.expectedMetricCount {
|
||||
t.Errorf("Got %v metrics, Want: %v metrics", len(ms), test.expectedMetricCount)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, metric := range ms {
|
||||
if metric.GetHelp() != test.expectedHelp {
|
||||
t.Errorf("Got %s as help message, want %s", metric.GetHelp(), test.expectedHelp)
|
||||
}
|
||||
}
|
||||
|
||||
// let's increment the counter and verify that the metric still works
|
||||
c.WithLabelValues("1", "3").Observe(1.0)
|
||||
c.WithLabelValues("2", "3").Observe(1.0)
|
||||
ms, err = registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather failed %v", err)
|
||||
}
|
||||
for _, mf := range ms {
|
||||
if len(mf.GetMetric()) != 3 {
|
||||
t.Errorf("Got %v metrics, wanted 2 as the count", len(mf.GetMetric()))
|
||||
}
|
||||
for _, m := range mf.GetMetric() {
|
||||
if m.GetSummary().GetSampleCount() != 1 {
|
||||
t.Errorf(
|
||||
"Got %v metrics, wanted 2 as the summary sample count",
|
||||
m.GetSummary().GetSampleCount())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -56,6 +56,16 @@ type CounterVecMetric interface {
|
||||
With(prometheus.Labels) CounterMetric
|
||||
}
|
||||
|
||||
// GaugeMetric is an interface which defines a subset of the interface provided by prometheus.Gauge
|
||||
type GaugeMetric interface {
|
||||
Set(float64)
|
||||
}
|
||||
|
||||
// ObserverMetric captures individual observations.
|
||||
type ObserverMetric interface {
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
// PromRegistry is an interface which implements a subset of prometheus.Registerer and
|
||||
// prometheus.Gatherer interfaces
|
||||
type PromRegistry interface {
|
||||
|
Loading…
Reference in New Issue
Block a user