Skip to content

Commit 2d92271

Browse files
authored
stats/opentelemetry: Add support for optional label grpc.lb.backend_service in per-call metrics (grpc#8637)
Addresses [A89](https://github.com/grpc/proposal/blob/master/A89-backend-service-metric-label.md) RELEASE NOTES: * stats/opentelemetry: Add support for optional label `grpc.lb.backend_service` in per-call metrics
1 parent 8110884 commit 2d92271

File tree

4 files changed

+19
-13
lines changed

4 files changed

+19
-13
lines changed

internal/xds/balancer/clusterimpl/clusterimpl.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ func (b *clusterImplBalancer) newPickerLocked() *picker {
163163
counter: b.requestCounter,
164164
countMax: b.requestCountMax,
165165
telemetryLabels: b.telemetryLabels,
166+
clusterName: b.clusterName,
166167
}
167168
}
168169

@@ -414,6 +415,7 @@ func (b *clusterImplBalancer) getClusterName() string {
414415
// SubConn to the wrapper for this purpose.
415416
type scWrapper struct {
416417
balancer.SubConn
418+
417419
// locality needs to be atomic because it can be updated while being read by
418420
// the picker.
419421
locality atomic.Pointer[clients.Locality]

internal/xds/balancer/clusterimpl/picker.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package clusterimpl
2020

2121
import (
2222
"context"
23+
"maps"
2324

2425
v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
2526
"google.golang.org/grpc/balancer"
@@ -87,6 +88,7 @@ type picker struct {
8788
counter *xdsclient.ClusterRequestsCounter
8889
countMax uint32
8990
telemetryLabels map[string]string
91+
clusterName string
9092
}
9193

9294
func telemetryLabels(ctx context.Context) map[string]string {
@@ -103,10 +105,9 @@ func telemetryLabels(ctx context.Context) map[string]string {
103105
func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
104106
// Unconditionally set labels if present, even dropped or queued RPC's can
105107
// use these labels.
106-
if labels := telemetryLabels(info.Ctx); labels != nil {
107-
for key, value := range d.telemetryLabels {
108-
labels[key] = value
109-
}
108+
labels := telemetryLabels(info.Ctx)
109+
if labels != nil {
110+
maps.Copy(labels, d.telemetryLabels)
110111
}
111112

112113
// Don't drop unless the inner picker is READY. Similar to
@@ -154,8 +155,9 @@ func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
154155
return pr, err
155156
}
156157

157-
if labels := telemetryLabels(info.Ctx); labels != nil {
158+
if labels != nil {
158159
labels["grpc.lb.locality"] = xdsinternal.LocalityString(lID)
160+
labels["grpc.lb.backend_service"] = d.clusterName
159161
}
160162

161163
if d.loadStore != nil {

stats/opentelemetry/client_metrics.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ func (h *clientMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInf
174174
// executes on the callpath that this OpenTelemetry component
175175
// currently supports.
176176
TelemetryLabels: map[string]string{
177-
"grpc.lb.locality": "",
177+
"grpc.lb.locality": "",
178+
"grpc.lb.backend_service": "",
178179
},
179180
}
180181
ctx = istats.SetLabels(ctx, labels)

test/xds/xds_telemetry_labels_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ const serviceNamespaceKey = "service_namespace"
4444
const serviceNamespaceKeyCSM = "csm.service_namespace_name"
4545
const serviceNameValue = "grpc-service"
4646
const serviceNamespaceValue = "grpc-service-namespace"
47-
47+
const backendServiceKey = "grpc.lb.backend_service"
48+
const backendServiceValue = "cluster-my-service-client-side-xds"
4849
const localityKey = "grpc.lb.locality"
4950
const localityValue = `{region="region-1", zone="zone-1", sub_zone="subzone-1"}`
5051

@@ -124,23 +125,23 @@ func (fsh *fakeStatsHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) co
124125

125126
func (fsh *fakeStatsHandler) HandleRPC(_ context.Context, rs stats.RPCStats) {
126127
switch rs.(type) {
127-
// stats.Begin won't get Telemetry Labels because happens after picker
128-
// picks.
129-
130-
// These three stats callouts trigger all metrics for OpenTelemetry that
131-
// aren't started. All of these should have access to the desired telemetry
128+
// stats.Begin is called before the picker runs, so it won't have telemetry
132129
// labels.
130+
// The following three stats callouts trigger OpenTelemetry metrics and are
131+
// guaranteed to run after the picker has selected a subchannel. Therefore,
132+
// they should have access to the desired telemetry labels.
133133
case *stats.OutPayload, *stats.InPayload, *stats.End:
134134
want := map[string]string{
135135
serviceNameKeyCSM: serviceNameValue,
136136
serviceNamespaceKeyCSM: serviceNamespaceValue,
137137
localityKey: localityValue,
138+
backendServiceKey: backendServiceValue,
138139
}
139140
if diff := cmp.Diff(fsh.labels.TelemetryLabels, want); diff != "" {
140141
fsh.t.Fatalf("fsh.labels.TelemetryLabels (-got +want): %v", diff)
141142
}
142143
default:
143144
// Nothing to assert for the other stats.Handler callouts.
144-
}
145145

146+
}
146147
}

0 commit comments

Comments
 (0)