Skip to content

Commit 7b19fac

Browse files
zasweqprintchard
authored andcommitted
internal/stats: Add metrics recorder list and usage in ClientConn (grpc#7428)
1 parent 38084ea commit 7b19fac

File tree

9 files changed

+583
-31
lines changed

9 files changed

+583
-31
lines changed

balancer/balancer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"google.golang.org/grpc/channelz"
3131
"google.golang.org/grpc/connectivity"
3232
"google.golang.org/grpc/credentials"
33+
estats "google.golang.org/grpc/experimental/stats"
3334
"google.golang.org/grpc/grpclog"
3435
"google.golang.org/grpc/internal"
3536
"google.golang.org/grpc/metadata"
@@ -256,6 +257,10 @@ type BuildOptions struct {
256257
// same resolver.Target as passed to the resolver. See the documentation for
257258
// the resolver.Target type for details about what it contains.
258259
Target resolver.Target
260+
// MetricsRecorder is the metrics recorder that balancers can use to record
261+
// metrics. Balancer implementations which do not register metrics on
262+
// metrics registry and record on them can ignore this field.
263+
MetricsRecorder estats.MetricsRecorder
259264
}
260265

261266
// Builder creates a balancer.

balancer_wrapper.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper {
8282
CustomUserAgent: cc.dopts.copts.UserAgent,
8383
ChannelzParent: cc.channelz,
8484
Target: cc.parsedTarget,
85+
MetricsRecorder: cc.metricsRecorderList,
8586
},
8687
serializer: grpcsync.NewCallbackSerializer(ctx),
8788
serializerCancel: cancel,

clientconn.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"google.golang.org/grpc/internal/grpcsync"
4141
"google.golang.org/grpc/internal/idle"
4242
iresolver "google.golang.org/grpc/internal/resolver"
43+
"google.golang.org/grpc/internal/stats"
4344
"google.golang.org/grpc/internal/transport"
4445
"google.golang.org/grpc/keepalive"
4546
"google.golang.org/grpc/resolver"
@@ -195,8 +196,11 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error)
195196
cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelz)
196197
cc.pickerWrapper = newPickerWrapper(cc.dopts.copts.StatsHandlers)
197198

199+
cc.metricsRecorderList = stats.NewMetricsRecorderList(cc.dopts.copts.StatsHandlers)
200+
198201
cc.initIdleStateLocked() // Safe to call without the lock, since nothing else has a reference to cc.
199202
cc.idlenessMgr = idle.NewManager((*idler)(cc), cc.dopts.idleTimeout)
203+
200204
return cc, nil
201205
}
202206

@@ -591,13 +595,14 @@ type ClientConn struct {
591595
cancel context.CancelFunc // Cancelled on close.
592596

593597
// The following are initialized at dial time, and are read-only after that.
594-
target string // User's dial target.
595-
parsedTarget resolver.Target // See initParsedTargetAndResolverBuilder().
596-
authority string // See initAuthority().
597-
dopts dialOptions // Default and user specified dial options.
598-
channelz *channelz.Channel // Channelz object.
599-
resolverBuilder resolver.Builder // See initParsedTargetAndResolverBuilder().
600-
idlenessMgr *idle.Manager
598+
target string // User's dial target.
599+
parsedTarget resolver.Target // See initParsedTargetAndResolverBuilder().
600+
authority string // See initAuthority().
601+
dopts dialOptions // Default and user specified dial options.
602+
channelz *channelz.Channel // Channelz object.
603+
resolverBuilder resolver.Builder // See initParsedTargetAndResolverBuilder().
604+
idlenessMgr *idle.Manager
605+
metricsRecorderList *stats.MetricsRecorderList
601606

602607
// The following provide their own synchronization, and therefore don't
603608
// require cc.mu to be held to access them.

experimental/stats/metricregistry.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ const (
8181
// on.
8282
type Int64CountHandle MetricDescriptor
8383

84+
// Descriptor returns the int64 count handle typecast to a pointer to a
85+
// MetricDescriptor.
86+
func (h *Int64CountHandle) Descriptor() *MetricDescriptor {
87+
return (*MetricDescriptor)(h)
88+
}
89+
8490
// Record records the int64 count value on the metrics recorder provided.
8591
func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
8692
recorder.RecordInt64Count(h, incr, labels...)
@@ -90,6 +96,12 @@ func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels .
9096
// passed at the recording point in order to know which metric to record on.
9197
type Float64CountHandle MetricDescriptor
9298

99+
// Descriptor returns the float64 count handle typecast to a pointer to a
100+
// MetricDescriptor.
101+
func (h *Float64CountHandle) Descriptor() *MetricDescriptor {
102+
return (*MetricDescriptor)(h)
103+
}
104+
93105
// Record records the float64 count value on the metrics recorder provided.
94106
func (h *Float64CountHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
95107
recorder.RecordFloat64Count(h, incr, labels...)
@@ -99,6 +111,12 @@ func (h *Float64CountHandle) Record(recorder MetricsRecorder, incr float64, labe
99111
// is passed at the recording point in order to know which metric to record on.
100112
type Int64HistoHandle MetricDescriptor
101113

114+
// Descriptor returns the int64 histo handle typecast to a pointer to a
115+
// MetricDescriptor.
116+
func (h *Int64HistoHandle) Descriptor() *MetricDescriptor {
117+
return (*MetricDescriptor)(h)
118+
}
119+
102120
// Record records the int64 histo value on the metrics recorder provided.
103121
func (h *Int64HistoHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
104122
recorder.RecordInt64Histo(h, incr, labels...)
@@ -109,6 +127,12 @@ func (h *Int64HistoHandle) Record(recorder MetricsRecorder, incr int64, labels .
109127
// record on.
110128
type Float64HistoHandle MetricDescriptor
111129

130+
// Descriptor returns the float64 histo handle typecast to a pointer to a
131+
// MetricDescriptor.
132+
func (h *Float64HistoHandle) Descriptor() *MetricDescriptor {
133+
return (*MetricDescriptor)(h)
134+
}
135+
112136
// Record records the float64 histo value on the metrics recorder provided.
113137
func (h *Float64HistoHandle) Record(recorder MetricsRecorder, incr float64, labels ...string) {
114138
recorder.RecordFloat64Histo(h, incr, labels...)
@@ -118,6 +142,12 @@ func (h *Float64HistoHandle) Record(recorder MetricsRecorder, incr float64, labe
118142
// passed at the recording point in order to know which metric to record on.
119143
type Int64GaugeHandle MetricDescriptor
120144

145+
// Descriptor returns the int64 gauge handle typecast to a pointer to a
146+
// MetricDescriptor.
147+
func (h *Int64GaugeHandle) Descriptor() *MetricDescriptor {
148+
return (*MetricDescriptor)(h)
149+
}
150+
121151
// Record records the int64 histo value on the metrics recorder provided.
122152
func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels ...string) {
123153
recorder.RecordInt64Gauge(h, incr, labels...)

experimental/stats/metricregistry_test.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -112,27 +112,27 @@ func (s) TestMetricRegistry(t *testing.T) {
112112
// The Metric Descriptor in the handle should be able to identify the metric
113113
// information. This is the key passed to metrics recorder to identify
114114
// metric.
115-
if got := fmr.intValues[(*MetricDescriptor)(intCountHandle1)]; got != 1 {
115+
if got := fmr.intValues[intCountHandle1.Descriptor()]; got != 1 {
116116
t.Fatalf("fmr.intValues[intCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
117117
}
118118

119119
floatCountHandle1.Record(fmr, 1.2, []string{"some label value", "some optional label value"}...)
120-
if got := fmr.floatValues[(*MetricDescriptor)(floatCountHandle1)]; got != 1.2 {
120+
if got := fmr.floatValues[floatCountHandle1.Descriptor()]; got != 1.2 {
121121
t.Fatalf("fmr.floatValues[floatCountHandle1.MetricDescriptor] got %v, want: %v", got, 1.2)
122122
}
123123

124124
intHistoHandle1.Record(fmr, 3, []string{"some label value", "some optional label value"}...)
125-
if got := fmr.intValues[(*MetricDescriptor)(intHistoHandle1)]; got != 3 {
125+
if got := fmr.intValues[intHistoHandle1.Descriptor()]; got != 3 {
126126
t.Fatalf("fmr.intValues[intHistoHandle1.MetricDescriptor] got %v, want: %v", got, 3)
127127
}
128128

129129
floatHistoHandle1.Record(fmr, 4.3, []string{"some label value", "some optional label value"}...)
130-
if got := fmr.floatValues[(*MetricDescriptor)(floatHistoHandle1)]; got != 4.3 {
130+
if got := fmr.floatValues[floatHistoHandle1.Descriptor()]; got != 4.3 {
131131
t.Fatalf("fmr.floatValues[floatHistoHandle1.MetricDescriptor] got %v, want: %v", got, 4.3)
132132
}
133133

134134
intGaugeHandle1.Record(fmr, 7, []string{"some label value", "some optional label value"}...)
135-
if got := fmr.intValues[(*MetricDescriptor)(intGaugeHandle1)]; got != 7 {
135+
if got := fmr.intValues[intGaugeHandle1.Descriptor()]; got != 7 {
136136
t.Fatalf("fmr.intValues[intGaugeHandle1.MetricDescriptor] got %v, want: %v", got, 7)
137137
}
138138
}
@@ -170,28 +170,28 @@ func TestNumerousIntCounts(t *testing.T) {
170170
fmr := newFakeMetricsRecorder(t)
171171

172172
intCountHandle1.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
173-
got := []int64{fmr.intValues[(*MetricDescriptor)(intCountHandle1)], fmr.intValues[(*MetricDescriptor)(intCountHandle2)], fmr.intValues[(*MetricDescriptor)(intCountHandle3)]}
173+
got := []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
174174
want := []int64{1, 0, 0}
175175
if diff := cmp.Diff(got, want); diff != "" {
176176
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
177177
}
178178

179179
intCountHandle2.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
180-
got = []int64{fmr.intValues[(*MetricDescriptor)(intCountHandle1)], fmr.intValues[(*MetricDescriptor)(intCountHandle2)], fmr.intValues[(*MetricDescriptor)(intCountHandle3)]}
180+
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
181181
want = []int64{1, 1, 0}
182182
if diff := cmp.Diff(got, want); diff != "" {
183183
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
184184
}
185185

186186
intCountHandle3.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
187-
got = []int64{fmr.intValues[(*MetricDescriptor)(intCountHandle1)], fmr.intValues[(*MetricDescriptor)(intCountHandle2)], fmr.intValues[(*MetricDescriptor)(intCountHandle3)]}
187+
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
188188
want = []int64{1, 1, 1}
189189
if diff := cmp.Diff(got, want); diff != "" {
190190
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
191191
}
192192

193193
intCountHandle3.Record(fmr, 1, []string{"some label value", "some optional label value"}...)
194-
got = []int64{fmr.intValues[(*MetricDescriptor)(intCountHandle1)], fmr.intValues[(*MetricDescriptor)(intCountHandle2)], fmr.intValues[(*MetricDescriptor)(intCountHandle3)]}
194+
got = []int64{fmr.intValues[intCountHandle1.Descriptor()], fmr.intValues[intCountHandle2.Descriptor()], fmr.intValues[intCountHandle3.Descriptor()]}
195195
want = []int64{1, 1, 2}
196196
if diff := cmp.Diff(got, want); diff != "" {
197197
t.Fatalf("fmr.intValues (-got, +want): %v", diff)
@@ -236,26 +236,26 @@ func verifyLabels(t *testing.T, labelsWant []string, optionalLabelsWant []string
236236
}
237237

238238
func (r *fakeMetricsRecorder) RecordInt64Count(handle *Int64CountHandle, incr int64, labels ...string) {
239-
verifyLabels(r.t, (*MetricDescriptor)(handle).Labels, (*MetricDescriptor)(handle).OptionalLabels, labels)
240-
r.intValues[(*MetricDescriptor)(handle)] += incr
239+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
240+
r.intValues[handle.Descriptor()] += incr
241241
}
242242

243243
func (r *fakeMetricsRecorder) RecordFloat64Count(handle *Float64CountHandle, incr float64, labels ...string) {
244-
verifyLabels(r.t, (*MetricDescriptor)(handle).Labels, (*MetricDescriptor)(handle).OptionalLabels, labels)
245-
r.floatValues[(*MetricDescriptor)(handle)] += incr
244+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
245+
r.floatValues[handle.Descriptor()] += incr
246246
}
247247

248248
func (r *fakeMetricsRecorder) RecordInt64Histo(handle *Int64HistoHandle, incr int64, labels ...string) {
249-
verifyLabels(r.t, (*MetricDescriptor)(handle).Labels, (*MetricDescriptor)(handle).OptionalLabels, labels)
250-
r.intValues[(*MetricDescriptor)(handle)] += incr
249+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
250+
r.intValues[handle.Descriptor()] += incr
251251
}
252252

253253
func (r *fakeMetricsRecorder) RecordFloat64Histo(handle *Float64HistoHandle, incr float64, labels ...string) {
254-
verifyLabels(r.t, (*MetricDescriptor)(handle).Labels, (*MetricDescriptor)(handle).OptionalLabels, labels)
255-
r.floatValues[(*MetricDescriptor)(handle)] += incr
254+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
255+
r.floatValues[handle.Descriptor()] += incr
256256
}
257257

258258
func (r *fakeMetricsRecorder) RecordInt64Gauge(handle *Int64GaugeHandle, incr int64, labels ...string) {
259-
verifyLabels(r.t, (*MetricDescriptor)(handle).Labels, (*MetricDescriptor)(handle).OptionalLabels, labels)
260-
r.intValues[(*MetricDescriptor)(handle)] += incr
259+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
260+
r.intValues[handle.Descriptor()] += incr
261261
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2024 gRPC authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package stats
18+
19+
import (
20+
"fmt"
21+
22+
estats "google.golang.org/grpc/experimental/stats"
23+
"google.golang.org/grpc/stats"
24+
)
25+
26+
// MetricsRecorderList forwards Record calls to all of its metricsRecorders.
27+
//
28+
// It eats any record calls where the label values provided do not match the
29+
// number of label keys.
30+
type MetricsRecorderList struct {
31+
// metricsRecorders are the metrics recorders this list will forward to.
32+
metricsRecorders []estats.MetricsRecorder
33+
}
34+
35+
// NewMetricsRecorderList creates a new metric recorder list with all the stats
36+
// handlers provided which implement the MetricsRecorder interface.
37+
// If no stats handlers provided implement the MetricsRecorder interface,
38+
// the MetricsRecorder list returned is a no-op.
39+
func NewMetricsRecorderList(shs []stats.Handler) *MetricsRecorderList {
40+
var mrs []estats.MetricsRecorder
41+
for _, sh := range shs {
42+
if mr, ok := sh.(estats.MetricsRecorder); ok {
43+
mrs = append(mrs, mr)
44+
}
45+
}
46+
return &MetricsRecorderList{
47+
metricsRecorders: mrs,
48+
}
49+
}
50+
51+
func verifyLabels(desc *estats.MetricDescriptor, labelsRecv ...string) {
52+
if got, want := len(labelsRecv), len(desc.Labels)+len(desc.OptionalLabels); got != want {
53+
panic(fmt.Sprintf("Received %d labels in call to record metric %q, but expected %d.", got, desc.Name, want))
54+
}
55+
}
56+
57+
func (l *MetricsRecorderList) RecordInt64Count(handle *estats.Int64CountHandle, incr int64, labels ...string) {
58+
verifyLabels(handle.Descriptor(), labels...)
59+
60+
for _, metricRecorder := range l.metricsRecorders {
61+
metricRecorder.RecordInt64Count(handle, incr, labels...)
62+
}
63+
}
64+
65+
func (l *MetricsRecorderList) RecordFloat64Count(handle *estats.Float64CountHandle, incr float64, labels ...string) {
66+
verifyLabels(handle.Descriptor(), labels...)
67+
68+
for _, metricRecorder := range l.metricsRecorders {
69+
metricRecorder.RecordFloat64Count(handle, incr, labels...)
70+
}
71+
}
72+
73+
func (l *MetricsRecorderList) RecordInt64Histo(handle *estats.Int64HistoHandle, incr int64, labels ...string) {
74+
verifyLabels(handle.Descriptor(), labels...)
75+
76+
for _, metricRecorder := range l.metricsRecorders {
77+
metricRecorder.RecordInt64Histo(handle, incr, labels...)
78+
}
79+
}
80+
81+
func (l *MetricsRecorderList) RecordFloat64Histo(handle *estats.Float64HistoHandle, incr float64, labels ...string) {
82+
verifyLabels(handle.Descriptor(), labels...)
83+
84+
for _, metricRecorder := range l.metricsRecorders {
85+
metricRecorder.RecordFloat64Histo(handle, incr, labels...)
86+
}
87+
}
88+
89+
func (l *MetricsRecorderList) RecordInt64Gauge(handle *estats.Int64GaugeHandle, incr int64, labels ...string) {
90+
verifyLabels(handle.Descriptor(), labels...)
91+
92+
for _, metricRecorder := range l.metricsRecorders {
93+
metricRecorder.RecordInt64Gauge(handle, incr, labels...)
94+
}
95+
}

0 commit comments

Comments
 (0)