snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
318
experimental/stats/metricregistry_test.go
Normal file
318
experimental/stats/metricregistry_test.go
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2024 gRPC 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 stats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
)
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
// TestPanic tests that registering two metrics with the same name across any
|
||||
// type of metric triggers a panic.
|
||||
func (s) TestPanic(t *testing.T) {
|
||||
cleanup := snapshotMetricsRegistryForTesting()
|
||||
defer cleanup()
|
||||
|
||||
want := "metric simple counter already registered"
|
||||
defer func() {
|
||||
if r := recover(); !strings.Contains(fmt.Sprint(r), want) {
|
||||
t.Errorf("expected panic contains %q, got %q", want, r)
|
||||
}
|
||||
}()
|
||||
desc := MetricDescriptor{
|
||||
// Type is not expected to be set from the registerer, but meant to be
|
||||
// set by the metric registry.
|
||||
Name: "simple counter",
|
||||
Description: "number of times recorded on tests",
|
||||
Unit: "{call}",
|
||||
}
|
||||
RegisterInt64Count(desc)
|
||||
RegisterInt64Gauge(desc)
|
||||
}
|
||||
|
||||
// TestInstrumentRegistry tests the metric registry. It registers testing only
|
||||
// metrics using the metric registry, and creates a fake metrics recorder which
|
||||
// uses these metrics. Using the handles returned from the metric registry, this
|
||||
// test records stats using the fake metrics recorder. Then, the test verifies
|
||||
// the persisted metrics data in the metrics recorder is what is expected. Thus,
|
||||
// this tests the interactions between the metrics recorder and the metrics
|
||||
// registry.
|
||||
func (s) TestMetricRegistry(t *testing.T) {
|
||||
cleanup := snapshotMetricsRegistryForTesting()
|
||||
defer cleanup()
|
||||
|
||||
intCountHandle1 := RegisterInt64Count(MetricDescriptor{
|
||||
Name: "simple counter",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int counter label"},
|
||||
OptionalLabels: []string{"int counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
floatCountHandle1 := RegisterFloat64Count(MetricDescriptor{
|
||||
Name: "float counter",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "float",
|
||||
Labels: []string{"float counter label"},
|
||||
OptionalLabels: []string{"float counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intHistoHandle1 := RegisterInt64Histo(MetricDescriptor{
|
||||
Name: "int histo",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int histo label"},
|
||||
OptionalLabels: []string{"int histo optional label"},
|
||||
Default: false,
|
||||
})
|
||||
floatHistoHandle1 := RegisterFloat64Histo(MetricDescriptor{
|
||||
Name: "float histo",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "float",
|
||||
Labels: []string{"float histo label"},
|
||||
OptionalLabels: []string{"float histo optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intGaugeHandle1 := RegisterInt64Gauge(MetricDescriptor{
|
||||
Name: "simple gauge",
|
||||
Description: "the most recent int emitted by test",
|
||||
Unit: "int",
|
||||
Labels: []string{"int gauge label"},
|
||||
OptionalLabels: []string{"int gauge optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
|
||||
Name: "simple up down counter",
|
||||
Description: "current number of emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int up down counter label"},
|
||||
OptionalLabels: []string{"int up down counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intAsyncGaugeHandle1 := RegisterInt64AsyncGauge(MetricDescriptor{
|
||||
Name: "simple async gauge",
|
||||
Description: "the most recent int emitted by test",
|
||||
Unit: "int",
|
||||
Labels: []string{"int async gauge label"},
|
||||
OptionalLabels: []string{"int async gauge optional label"},
|
||||
Default: false,
|
||||
})
|
||||
|
||||
fmr := newFakeMetricsRecorder(t)
|
||||
|
||||
intCountHandle1.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
|
||||
// The Metric Descriptor in the handle should be able to identify the metric
|
||||
// information. This is the key passed to metrics recorder to identify
|
||||
// metric.
|
||||
if got := fmr.intValues[intCountHandle1.Descriptor()]; got != 1 {
|
||||
t.Fatalf("fmr.intValues[intCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
|
||||
}
|
||||
|
||||
intUpDownCountHandle1.Record(fmr, 2, []string{"some label value", "some optional label value"}...)
|
||||
// The Metric Descriptor in the handle should be able to identify the metric
|
||||
// information. This is the key passed to metrics recorder to identify
|
||||
// metric.
|
||||
if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 2 {
|
||||
t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 2)
|
||||
}
|
||||
|
||||
floatCountHandle1.Record(fmr, 1.2, []string{"some label value", "some optional label value"}...)
|
||||
if got := fmr.floatValues[floatCountHandle1.Descriptor()]; got != 1.2 {
|
||||
t.Fatalf("fmr.floatValues[floatCountHandle1.MetricDescriptor] got %v, want: %v", got, 1.2)
|
||||
}
|
||||
|
||||
intHistoHandle1.Record(fmr, 3, []string{"some label value", "some optional label value"}...)
|
||||
if got := fmr.intValues[intHistoHandle1.Descriptor()]; got != 3 {
|
||||
t.Fatalf("fmr.intValues[intHistoHandle1.MetricDescriptor] got %v, want: %v", got, 3)
|
||||
}
|
||||
|
||||
floatHistoHandle1.Record(fmr, 4.3, []string{"some label value", "some optional label value"}...)
|
||||
if got := fmr.floatValues[floatHistoHandle1.Descriptor()]; got != 4.3 {
|
||||
t.Fatalf("fmr.floatValues[floatHistoHandle1.MetricDescriptor] got %v, want: %v", got, 4.3)
|
||||
}
|
||||
|
||||
intGaugeHandle1.Record(fmr, 7, []string{"some label value", "some optional label value"}...)
|
||||
if got := fmr.intValues[intGaugeHandle1.Descriptor()]; got != 7 {
|
||||
t.Fatalf("fmr.intValues[intGaugeHandle1.MetricDescriptor] got %v, want: %v", got, 7)
|
||||
}
|
||||
intAsyncGaugeHandle1.Record(fmr, 9, []string{"some label value", "some optional label value"}...)
|
||||
if got := fmr.intValues[intAsyncGaugeHandle1.Descriptor()]; got != 9 {
|
||||
t.Fatalf("fmr.intValues[intAsyncGaugeHandle1.MetricDescriptor] got %v, want: %v", got, 9)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestUpDownCounts(t *testing.T) {
|
||||
cleanup := snapshotMetricsRegistryForTesting()
|
||||
defer cleanup()
|
||||
|
||||
intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
|
||||
Name: "simple up down counter",
|
||||
Description: "current number of emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int up down counter label"},
|
||||
OptionalLabels: []string{"int up down counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
|
||||
fmr := newFakeMetricsRecorder(t)
|
||||
intUpDownCountHandle1.Record(fmr, 2, []string{"up down value", "some optional label value"}...)
|
||||
intUpDownCountHandle1.Record(fmr, -1, []string{"up down value", "some optional label value"}...)
|
||||
|
||||
if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 1 {
|
||||
t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNumerousIntCounts tests numerous int count metrics registered onto the
|
||||
// metric registry. A component (simulated by test) should be able to record on
|
||||
// the different registered int count metrics.
|
||||
func (s) TestNumerousIntCounts(t *testing.T) {
|
||||
cleanup := snapshotMetricsRegistryForTesting()
|
||||
defer cleanup()
|
||||
|
||||
intCountHandle1 := RegisterInt64Count(MetricDescriptor{
|
||||
Name: "int counter",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int counter label"},
|
||||
OptionalLabels: []string{"int counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intCountHandle2 := RegisterInt64Count(MetricDescriptor{
|
||||
Name: "int counter 2",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int counter label"},
|
||||
OptionalLabels: []string{"int counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
intCountHandle3 := RegisterInt64Count(MetricDescriptor{
|
||||
Name: "int counter 3",
|
||||
Description: "sum of all emissions from tests",
|
||||
Unit: "int",
|
||||
Labels: []string{"int counter label"},
|
||||
OptionalLabels: []string{"int counter optional label"},
|
||||
Default: false,
|
||||
})
|
||||
|
||||
fmr := newFakeMetricsRecorder(t)
|
||||
|
||||
intCountHandle1.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
|
||||
got := []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
|
||||
want := []int64{1, 0, 0}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
|
||||
}
|
||||
|
||||
intCountHandle2.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
|
||||
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
|
||||
want = []int64{1, 1, 0}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
|
||||
}
|
||||
|
||||
intCountHandle3.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
|
||||
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
|
||||
want = []int64{1, 1, 1}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
|
||||
}
|
||||
|
||||
intCountHandle3.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
|
||||
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
|
||||
want = []int64{1, 1, 2}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeMetricsRecorder struct {
|
||||
UnimplementedMetricsRecorder
|
||||
t *testing.T
|
||||
|
||||
intValues map[*MetricDescriptor]int64
|
||||
floatValues map[*MetricDescriptor]float64
|
||||
}
|
||||
|
||||
// newFakeMetricsRecorder returns a fake metrics recorder based off the current
|
||||
// state of global metric registry.
|
||||
func newFakeMetricsRecorder(t *testing.T) *fakeMetricsRecorder {
|
||||
fmr := &fakeMetricsRecorder{
|
||||
t: t,
|
||||
intValues: make(map[*MetricDescriptor]int64),
|
||||
floatValues: make(map[*MetricDescriptor]float64),
|
||||
}
|
||||
return fmr
|
||||
}
|
||||
|
||||
// verifyLabels verifies that the labels received are of the expected length.
|
||||
func verifyLabels(t *testing.T, labelsWant []string, optionalLabelsWant []string, labelsGot []string) {
|
||||
if len(labelsWant)+len(optionalLabelsWant) != len(labelsGot) {
|
||||
t.Fatalf("length of optional labels expected did not match got %v, want %v", len(labelsGot), len(labelsWant)+len(optionalLabelsWant))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordInt64Count(handle *Int64CountHandle, incr int64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.intValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordFloat64Count(handle *Float64CountHandle, incr float64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.floatValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordInt64Histo(handle *Int64HistoHandle, incr int64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.intValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordFloat64Histo(handle *Float64HistoHandle, incr float64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.floatValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordInt64Gauge(handle *Int64GaugeHandle, incr int64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.intValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
r.intValues[handle.Descriptor()] += incr
|
||||
}
|
||||
|
||||
func (r *fakeMetricsRecorder) RecordInt64AsyncGauge(handle *Int64AsyncGaugeHandle, val int64, labels ...string) {
|
||||
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
|
||||
// Async gauges in OTel are "Observer" instruments; they report
|
||||
// the current state of the world every cycle, they do not accumulate deltas.
|
||||
r.intValues[handle.Descriptor()] = val
|
||||
}
|
||||
Reference in New Issue
Block a user