From 206540e4cf75a29904378e83360dfb83d9144fe7 Mon Sep 17 00:00:00 2001 From: Lucas Meijer Date: Tue, 3 Mar 2026 11:23:20 +0100 Subject: [PATCH] grpc: rename tracing config fields and make follow-up stream tracing opt-in --- pkg/grpc/proto_trace_attributes_extractor.go | 144 +++++++---- .../proto_trace_attributes_extractor_test.go | 228 +++++++++++++++++- pkg/proto/configuration/grpc/grpc.pb.go | 35 ++- pkg/proto/configuration/grpc/grpc.proto | 25 +- 4 files changed, 360 insertions(+), 72 deletions(-) diff --git a/pkg/grpc/proto_trace_attributes_extractor.go b/pkg/grpc/proto_trace_attributes_extractor.go index ebcf9337..d5fd3ef8 100644 --- a/pkg/grpc/proto_trace_attributes_extractor.go +++ b/pkg/grpc/proto_trace_attributes_extractor.go @@ -39,9 +39,10 @@ func NewProtoTraceAttributesExtractor(configuration map[string]*configuration.Tr } for methodName, methodConfiguration := range configuration { pe.methods[methodName] = &methodTraceAttributesExtractor{ - errorLogger: errorLogger, - requestAttributes: methodConfiguration.AttributesFromFirstRequestMessage, - responseAttributes: methodConfiguration.AttributesFromFirstResponseMessage, + errorLogger: errorLogger, + requestAttributes: methodConfiguration.AttributesFromRequestMessage, + responseAttributes: methodConfiguration.AttributesFromResponseMessage, + includeFollowupMessages: methodConfiguration.IncludeFollowupMessages, } } return pe @@ -66,10 +67,10 @@ func (pe *ProtoTraceAttributesExtractor) InterceptUnaryClient(ctx context.Contex if span == nil { return invoker(ctx, method, req, reply, cc, opts...) } - me.processRequest(span, req) + me.applyAttributes(span, me.requestAttributesFor(req)) err := invoker(ctx, method, req, reply, cc, opts...) if err == nil { - me.processResponse(span, reply) + me.applyAttributes(span, me.responseAttributesFor(reply)) } return err } @@ -113,10 +114,10 @@ func (pe *ProtoTraceAttributesExtractor) InterceptUnaryServer(ctx context.Contex if span == nil { return handler(ctx, req) } - me.processRequest(span, req) + me.applyAttributes(span, me.requestAttributesFor(req)) resp, err := handler(ctx, req) if err == nil { - me.processResponse(span, resp) + me.applyAttributes(span, me.responseAttributesFor(resp)) } return resp, err } @@ -156,24 +157,26 @@ type methodTraceAttributesExtractor struct { responseAttributes []string responseOnce sync.Once responseExtractor directionTraceAttributesExtractor + + includeFollowupMessages bool } -func (me *methodTraceAttributesExtractor) processRequest(span trace.Span, req interface{}) { +func (me *methodTraceAttributesExtractor) requestAttributesFor(req interface{}) []attribute.KeyValue { me.requestOnce.Do(func() { // First time we see an RPC message going from the // client to the server. me.requestExtractor.initialize("request", me.requestAttributes, req, me.errorLogger) }) - me.requestExtractor.gatherAttributes(span, req) + return me.requestExtractor.extractAttributes(req) } -func (me *methodTraceAttributesExtractor) processResponse(span trace.Span, resp interface{}) { +func (me *methodTraceAttributesExtractor) responseAttributesFor(resp interface{}) []attribute.KeyValue { me.responseOnce.Do(func() { // First time we see an RPC message going from the // server to the client. me.responseExtractor.initialize("response", me.responseAttributes, resp, me.errorLogger) }) - me.responseExtractor.gatherAttributes(span, resp) + return me.responseExtractor.extractAttributes(resp) } // methodTraceAttributesExtractor is the bookkeeping that needs to be @@ -199,15 +202,16 @@ func (de *directionTraceAttributesExtractor) initialize(attributePrefix string, } } -func (de *directionTraceAttributesExtractor) gatherAttributes(span trace.Span, m interface{}) { - if len(de.attributeExtractors) > 0 { - mProtoReflect := m.(proto.Message).ProtoReflect() - attributes := make([]attribute.KeyValue, 0, len(de.attributeExtractors)) - for _, attributeExtractor := range de.attributeExtractors { - attributes = attributeExtractor(mProtoReflect, attributes) - } - span.SetAttributes(attributes...) +func (de *directionTraceAttributesExtractor) extractAttributes(m interface{}) []attribute.KeyValue { + if len(de.attributeExtractors) == 0 { + return nil + } + mProtoReflect := m.(proto.Message).ProtoReflect() + attributes := make([]attribute.KeyValue, 0, len(de.attributeExtractors)) + for _, attributeExtractor := range de.attributeExtractors { + attributes = attributeExtractor(mProtoReflect, attributes) } + return attributes } // attributeExtractor is a function type that is capable of extracting a @@ -392,63 +396,121 @@ func newAttributeExtractor(descriptor protoreflect.MessageDescriptor, remainingF } // attributeExtractingClientStream is a decorator for grpc.ClientStream -// that extracts trace span attributes from the first request and -// response message in a streaming RPC. +// that extracts trace span attributes from streaming request and +// response messages. type attributeExtractingClientStream struct { grpc.ClientStream method *methodTraceAttributesExtractor span trace.Span gotFirstRequest bool gotFirstResponse bool + requestIndex uint64 + responseIndex uint64 } func (cs *attributeExtractingClientStream) SendMsg(m interface{}) error { - if !cs.gotFirstRequest { + isFirstMessage := !cs.gotFirstRequest + if isFirstMessage { cs.gotFirstRequest = true - cs.method.processRequest(cs.span, m) + } else if !cs.method.includeFollowupMessages { + return cs.ClientStream.SendMsg(m) + } + attributes := cs.method.requestAttributesFor(m) + if isFirstMessage { + cs.method.applyAttributes(cs.span, attributes) + } + err := cs.ClientStream.SendMsg(m) + if err == nil { + cs.requestIndex++ + addMessageEvent(cs.span, "out", cs.requestIndex, attributes) } - return cs.ClientStream.SendMsg(m) + return err } func (cs *attributeExtractingClientStream) RecvMsg(m interface{}) error { - if !cs.gotFirstResponse { - if err := cs.ClientStream.RecvMsg(m); err != nil { - return err - } + if err := cs.ClientStream.RecvMsg(m); err != nil { + return err + } + isFirstMessage := !cs.gotFirstResponse + if isFirstMessage { cs.gotFirstResponse = true - cs.method.processResponse(cs.span, m) + } else if !cs.method.includeFollowupMessages { return nil } - return cs.ClientStream.RecvMsg(m) + attributes := cs.method.responseAttributesFor(m) + if isFirstMessage { + cs.method.applyAttributes(cs.span, attributes) + } + cs.responseIndex++ + addMessageEvent(cs.span, "in", cs.responseIndex, attributes) + return nil } // attributeExtractingServerStream is a decorator for grpc.ServerStream -// that extracts trace span attributes from the first request and -// response message in a streaming RPC. +// that extracts trace span attributes from streaming request and +// response messages. type attributeExtractingServerStream struct { grpc.ServerStream method *methodTraceAttributesExtractor span trace.Span gotFirstRequest bool gotFirstResponse bool + requestIndex uint64 + responseIndex uint64 } func (cs *attributeExtractingServerStream) RecvMsg(m interface{}) error { - if !cs.gotFirstRequest { - if err := cs.ServerStream.RecvMsg(m); err != nil { - return err - } + if err := cs.ServerStream.RecvMsg(m); err != nil { + return err + } + isFirstMessage := !cs.gotFirstRequest + if isFirstMessage { cs.gotFirstRequest = true - cs.method.processRequest(cs.span, m) + } else if !cs.method.includeFollowupMessages { return nil } - return cs.ServerStream.RecvMsg(m) + attributes := cs.method.requestAttributesFor(m) + if isFirstMessage { + cs.method.applyAttributes(cs.span, attributes) + } + cs.requestIndex++ + addMessageEvent(cs.span, "in", cs.requestIndex, attributes) + return nil } func (cs *attributeExtractingServerStream) SendMsg(m interface{}) error { - if !cs.gotFirstResponse { + isFirstMessage := !cs.gotFirstResponse + if isFirstMessage { cs.gotFirstResponse = true - cs.method.processResponse(cs.span, m) + } else if !cs.method.includeFollowupMessages { + return cs.ServerStream.SendMsg(m) + } + attributes := cs.method.responseAttributesFor(m) + if isFirstMessage { + cs.method.applyAttributes(cs.span, attributes) + } + err := cs.ServerStream.SendMsg(m) + if err == nil { + cs.responseIndex++ + addMessageEvent(cs.span, "out", cs.responseIndex, attributes) + } + return err +} + +func (methodTraceAttributesExtractor) applyAttributes(span trace.Span, attributes []attribute.KeyValue) { + if span == nil || !span.IsRecording() || len(attributes) == 0 { + return + } + span.SetAttributes(attributes...) +} + +func addMessageEvent(span trace.Span, direction string, index uint64, attributes []attribute.KeyValue) { + if span == nil || !span.IsRecording() { + return } - return cs.ServerStream.SendMsg(m) + attributes = append(attributes, + attribute.String("grpc.message.direction", direction), + attribute.Int64("grpc.message.index", int64(index)), + ) + span.AddEvent("grpc.message", trace.WithAttributes(attributes...)) } diff --git a/pkg/grpc/proto_trace_attributes_extractor_test.go b/pkg/grpc/proto_trace_attributes_extractor_test.go index 04c3a1d9..c6986d24 100644 --- a/pkg/grpc/proto_trace_attributes_extractor_test.go +++ b/pkg/grpc/proto_trace_attributes_extractor_test.go @@ -3,6 +3,7 @@ package grpc_test import ( "context" "encoding/base64" + "reflect" "testing" remoteexecution "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" @@ -24,6 +25,28 @@ import ( "go.uber.org/mock/gomock" ) +func hasAttribute(attributes []attribute.KeyValue, want attribute.KeyValue) bool { + for _, attribute := range attributes { + if reflect.DeepEqual(attribute, want) { + return true + } + } + return false +} + +func findEventAttributes(t *testing.T, events []trace.EventConfig, direction string, index int64) []attribute.KeyValue { + t.Helper() + for _, event := range events { + attributes := event.Attributes() + if hasAttribute(attributes, attribute.String("grpc.message.direction", direction)) && + hasAttribute(attributes, attribute.Int64("grpc.message.index", index)) { + return attributes + } + } + t.Fatalf("event not found for direction=%q index=%d", direction, index) + return nil +} + func TestProtoTraceAttributesExtractor(t *testing.T) { ctrl, ctx := gomock.WithContext(context.Background(), t) @@ -48,7 +71,7 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { extractor := bb_grpc.NewProtoTraceAttributesExtractor(map[string]*configuration.TracingMethodConfiguration{ fullMethod: { - AttributesFromFirstResponseMessage: []string{ + AttributesFromResponseMessage: []string{ "bloom_filter", "bloom_filter_hash_functions", }, @@ -56,7 +79,7 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { }, mock.NewMockErrorLogger(ctrl)) handler := mock.NewMockUnaryHandler(ctrl) - span.EXPECT().IsRecording().Return(true) + span.EXPECT().IsRecording().Return(true).AnyTimes() handler.EXPECT().Call(ctxWithSpan, request).Return(response, nil) span.EXPECT().SetAttributes([]attribute.KeyValue{ attribute.String("response.bloom_filter", base64.StdEncoding.EncodeToString([]byte{0x01, 0x02, 0x03})), @@ -91,14 +114,14 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { errorLogger := mock.NewMockErrorLogger(ctrl) extractor := bb_grpc.NewProtoTraceAttributesExtractor(map[string]*configuration.TracingMethodConfiguration{ exampleUnaryFullMethod: { - AttributesFromFirstRequestMessage: []string{ + AttributesFromRequestMessage: []string{ // Valid: "instance_name", // Invalid. "", }, - AttributesFromFirstResponseMessage: []string{ + AttributesFromResponseMessage: []string{ // Valid. "cache_capabilities.digest_functions", "cache_capabilities.max_batch_total_size_bytes", @@ -156,6 +179,8 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { // If the span is not recording, we shouldn't // spend any effort inspecting the request and // response. + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) span.EXPECT().IsRecording().Return(false) var observedResponse remoteexecution.ServerCapabilities invoker.EXPECT().Call(ctxWithSpan, exampleUnaryFullMethod, exampleUnaryRequest, &observedResponse, nil). @@ -173,7 +198,9 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { // should use protoreflect to create extractors // for each of the attributes. This should cause // any configuration errors to be reported. - span.EXPECT().IsRecording().Return(true) + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) + span.EXPECT().IsRecording().Return(true).AnyTimes() errorLogger.EXPECT().Log(testutil.EqStatus(t, status.Error(codes.InvalidArgument, "Failed to create extractor for attribute \"request\": Attribute name does not contain any fields"))) errorLogger.EXPECT().Log(testutil.EqStatus(t, status.Error(codes.InvalidArgument, "Failed to create extractor for attribute \"response.cache_capabilities\": Field \"cache_capabilities\" does not have a boolean, enumeration, floating point, integer, bytes or string type"))) errorLogger.EXPECT().Log(testutil.EqStatus(t, status.Error(codes.InvalidArgument, "Failed to create extractor for attribute \"response.nonexistent\": Field \"nonexistent\" does not exist"))) @@ -218,6 +245,8 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { // Methods for which we've not specified a // configuration should have their calls // forwarded in literal form. + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) request := &remoteexecution.UpdateActionResultRequest{ InstanceName: "default-scheduler", } @@ -235,6 +264,8 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { // If the span is not recording, we shouldn't // spend any effort inspecting the request and // response. + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) span.EXPECT().IsRecording().Return(false) handler.EXPECT().Call(ctxWithSpan, exampleUnaryRequest).Return(exampleUnaryResponse, nil) @@ -248,7 +279,9 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { t.Run("RecordingSpan", func(t *testing.T) { // As the span is recording, we should see calls // to SetAttributes(). - span.EXPECT().IsRecording().Return(true) + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) + span.EXPECT().IsRecording().Return(true).AnyTimes() span.EXPECT().SetAttributes([]attribute.KeyValue{ attribute.String("request.instance_name", "default-scheduler"), }) @@ -267,6 +300,187 @@ func TestProtoTraceAttributesExtractor(t *testing.T) { testutil.RequireEqualProto(t, exampleUnaryResponse, observedResponse.(proto.Message)) }) }) +} + +func TestProtoTraceAttributesExtractorStreaming(t *testing.T) { + ctrl, ctx := gomock.WithContext(context.Background(), t) + + streamMethod := "/build.bazel.remote.execution.v2.Capabilities/StreamCapabilities" + streamDesc := grpc.StreamDesc{StreamName: "StreamCapabilities", ClientStreams: true, ServerStreams: true} + + newExtractor := func(includeFollowupMessages bool) *bb_grpc.ProtoTraceAttributesExtractor { + methodConfiguration := &configuration.TracingMethodConfiguration{ + AttributesFromRequestMessage: []string{ + "instance_name", + }, + AttributesFromResponseMessage: []string{ + "execution_capabilities.exec_enabled", + }, + IncludeFollowupMessages: includeFollowupMessages, + } + return bb_grpc.NewProtoTraceAttributesExtractor(map[string]*configuration.TracingMethodConfiguration{ + streamMethod: methodConfiguration, + }, mock.NewMockErrorLogger(ctrl)) + } + + collectEvents := func(span *mock.WrappedMockSpan) *[]trace.EventConfig { + events := []trace.EventConfig{} + span.EXPECT().AddEvent("grpc.message", gomock.Any()).AnyTimes().Do( + func(_ string, options ...trace.EventOption) { + events = append(events, trace.NewEventConfig(options...)) + }) + return &events + } + + assertClientEvents := func(t *testing.T, events []trace.EventConfig, includeFollowupMessages bool) { + t.Helper() + attributes := findEventAttributes(t, events, "out", 1) + require.True(t, hasAttribute(attributes, attribute.String("request.instance_name", "default-scheduler"))) + attributes = findEventAttributes(t, events, "in", 1) + require.True(t, hasAttribute(attributes, attribute.Bool("response.execution_capabilities.exec_enabled", true))) + if includeFollowupMessages { + attributes = findEventAttributes(t, events, "out", 2) + require.True(t, hasAttribute(attributes, attribute.String("request.instance_name", "default-scheduler"))) + attributes = findEventAttributes(t, events, "in", 2) + require.True(t, hasAttribute(attributes, attribute.Bool("response.execution_capabilities.exec_enabled", true))) + } + } + + assertServerEvents := func(t *testing.T, events []trace.EventConfig, includeFollowupMessages bool) { + t.Helper() + attributes := findEventAttributes(t, events, "in", 1) + require.True(t, hasAttribute(attributes, attribute.String("request.instance_name", "first"))) + attributes = findEventAttributes(t, events, "out", 1) + require.True(t, hasAttribute(attributes, attribute.Bool("response.execution_capabilities.exec_enabled", true))) + if includeFollowupMessages { + attributes = findEventAttributes(t, events, "in", 2) + require.True(t, hasAttribute(attributes, attribute.String("request.instance_name", "second"))) + attributes = findEventAttributes(t, events, "out", 2) + require.True(t, hasAttribute(attributes, attribute.Bool("response.execution_capabilities.exec_enabled", false))) + } + } + + t.Run("InterceptStreamClient", func(t *testing.T) { + request := &remoteexecution.GetCapabilitiesRequest{InstanceName: "default-scheduler"} + response := &remoteexecution.ServerCapabilities{ + ExecutionCapabilities: &remoteexecution.ExecutionCapabilities{ExecEnabled: true}, + } + + for _, testCase := range []struct { + name string + includeFollowupMessages bool + expectedEventCount int + }{ + {name: "WithFollowupMessages", includeFollowupMessages: true, expectedEventCount: 4}, + {name: "WithoutFollowupMessages", includeFollowupMessages: false, expectedEventCount: 2}, + } { + t.Run(testCase.name, func(t *testing.T) { + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) + streamer := mock.NewMockStreamer(ctrl) + clientStream := mock.NewMockClientStream(ctrl) + + extractor := newExtractor(testCase.includeFollowupMessages) + + span.EXPECT().IsRecording().Return(true).AnyTimes() + streamer.EXPECT().Call(ctxWithSpan, &streamDesc, nil, streamMethod).Return(clientStream, nil) + clientStream.EXPECT().SendMsg(request).Return(nil).Times(2) + clientStream.EXPECT().RecvMsg(gomock.Any()).DoAndReturn( + func(m interface{}) error { + proto.Merge(m.(proto.Message), response) + return nil + }).Times(2) + + span.EXPECT().SetAttributes([]attribute.KeyValue{ + attribute.String("request.instance_name", "default-scheduler"), + }) + span.EXPECT().SetAttributes([]attribute.KeyValue{ + attribute.Bool("response.execution_capabilities.exec_enabled", true), + }) + + events := collectEvents(span) + + wrappedStream, err := extractor.InterceptStreamClient(ctxWithSpan, &streamDesc, nil, streamMethod, streamer.Call) + require.NoError(t, err) + require.NoError(t, wrappedStream.SendMsg(request)) + require.NoError(t, wrappedStream.SendMsg(request)) + var observedResponse remoteexecution.ServerCapabilities + require.NoError(t, wrappedStream.RecvMsg(&observedResponse)) + require.NoError(t, wrappedStream.RecvMsg(&observedResponse)) + + require.Len(t, *events, testCase.expectedEventCount) + assertClientEvents(t, *events, testCase.includeFollowupMessages) + }) + } + }) + + t.Run("InterceptStreamServer", func(t *testing.T) { + requestOne := &remoteexecution.GetCapabilitiesRequest{InstanceName: "first"} + requestTwo := &remoteexecution.GetCapabilitiesRequest{InstanceName: "second"} + responseOne := &remoteexecution.ServerCapabilities{ + ExecutionCapabilities: &remoteexecution.ExecutionCapabilities{ExecEnabled: true}, + } + responseTwo := &remoteexecution.ServerCapabilities{ + ExecutionCapabilities: &remoteexecution.ExecutionCapabilities{ExecEnabled: false}, + } + + for _, testCase := range []struct { + name string + includeFollowupMessages bool + expectedEventCount int + }{ + {name: "WithFollowupMessages", includeFollowupMessages: true, expectedEventCount: 4}, + {name: "WithoutFollowupMessages", includeFollowupMessages: false, expectedEventCount: 2}, + } { + t.Run(testCase.name, func(t *testing.T) { + span := mock.NewMockSpan(ctrl) + ctxWithSpan := trace.ContextWithSpan(ctx, span) + serverStream := mock.NewMockServerStream(ctrl) + handler := mock.NewMockStreamHandler(ctrl) + + extractor := newExtractor(testCase.includeFollowupMessages) + + span.EXPECT().IsRecording().Return(true).AnyTimes() + serverStream.EXPECT().Context().Return(ctxWithSpan).AnyTimes() + serverStream.EXPECT().RecvMsg(gomock.Any()).DoAndReturn( + func(m interface{}) error { + proto.Merge(m.(proto.Message), requestOne) + return nil + }) + serverStream.EXPECT().RecvMsg(gomock.Any()).DoAndReturn( + func(m interface{}) error { + proto.Merge(m.(proto.Message), requestTwo) + return nil + }) + serverStream.EXPECT().SendMsg(responseOne).Return(nil) + serverStream.EXPECT().SendMsg(responseTwo).Return(nil) + + span.EXPECT().SetAttributes([]attribute.KeyValue{ + attribute.String("request.instance_name", "first"), + }) + span.EXPECT().SetAttributes([]attribute.KeyValue{ + attribute.Bool("response.execution_capabilities.exec_enabled", true), + }) - // TODO: Add testing coverage for streaming RPCs. + events := collectEvents(span) + + handler.EXPECT().Call(nil, gomock.Any()).DoAndReturn( + func(srv interface{}, stream grpc.ServerStream) error { + var observedRequest remoteexecution.GetCapabilitiesRequest + require.NoError(t, stream.RecvMsg(&observedRequest)) + require.NoError(t, stream.RecvMsg(&observedRequest)) + require.NoError(t, stream.SendMsg(responseOne)) + require.NoError(t, stream.SendMsg(responseTwo)) + return nil + }) + + require.NoError(t, extractor.InterceptStreamServer(nil, serverStream, &grpc.StreamServerInfo{ + FullMethod: streamMethod, + }, handler.Call)) + + require.Len(t, *events, testCase.expectedEventCount) + assertServerEvents(t, *events, testCase.includeFollowupMessages) + }) + } + }) } diff --git a/pkg/proto/configuration/grpc/grpc.pb.go b/pkg/proto/configuration/grpc/grpc.pb.go index 67872d01..467eee38 100644 --- a/pkg/proto/configuration/grpc/grpc.pb.go +++ b/pkg/proto/configuration/grpc/grpc.pb.go @@ -924,11 +924,12 @@ func (x *RemoteAuthenticationPolicy) GetCacheReplacementPolicy() eviction.CacheR } type TracingMethodConfiguration struct { - state protoimpl.MessageState `protogen:"open.v1"` - AttributesFromFirstRequestMessage []string `protobuf:"bytes,1,rep,name=attributes_from_first_request_message,json=attributesFromFirstRequestMessage,proto3" json:"attributes_from_first_request_message,omitempty"` - AttributesFromFirstResponseMessage []string `protobuf:"bytes,2,rep,name=attributes_from_first_response_message,json=attributesFromFirstResponseMessage,proto3" json:"attributes_from_first_response_message,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + AttributesFromRequestMessage []string `protobuf:"bytes,1,rep,name=attributes_from_request_message,json=attributesFromRequestMessage,proto3" json:"attributes_from_request_message,omitempty"` + AttributesFromResponseMessage []string `protobuf:"bytes,2,rep,name=attributes_from_response_message,json=attributesFromResponseMessage,proto3" json:"attributes_from_response_message,omitempty"` + IncludeFollowupMessages bool `protobuf:"varint,3,opt,name=include_followup_messages,json=includeFollowupMessages,proto3" json:"include_followup_messages,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *TracingMethodConfiguration) Reset() { @@ -961,20 +962,27 @@ func (*TracingMethodConfiguration) Descriptor() ([]byte, []int) { return file_github_com_buildbarn_bb_storage_pkg_proto_configuration_grpc_grpc_proto_rawDescGZIP(), []int{10} } -func (x *TracingMethodConfiguration) GetAttributesFromFirstRequestMessage() []string { +func (x *TracingMethodConfiguration) GetAttributesFromRequestMessage() []string { if x != nil { - return x.AttributesFromFirstRequestMessage + return x.AttributesFromRequestMessage } return nil } -func (x *TracingMethodConfiguration) GetAttributesFromFirstResponseMessage() []string { +func (x *TracingMethodConfiguration) GetAttributesFromResponseMessage() []string { if x != nil { - return x.AttributesFromFirstResponseMessage + return x.AttributesFromResponseMessage } return nil } +func (x *TracingMethodConfiguration) GetIncludeFollowupMessages() bool { + if x != nil { + return x.IncludeFollowupMessages + } + return false +} + type ServerRelayConfiguration struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint *ClientConfiguration `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` @@ -1159,10 +1167,11 @@ const file_github_com_buildbarn_bb_storage_pkg_proto_configuration_grpc_grpc_pro "\bendpoint\x18\x02 \x01(\v21.buildbarn.configuration.grpc.ClientConfigurationR\bendpoint\x12,\n" + "\x05scope\x18\x03 \x01(\v2\x16.google.protobuf.ValueR\x05scope\x12,\n" + "\x12maximum_cache_size\x18\x04 \x01(\x05R\x10maximumCacheSize\x12r\n" + - "\x18cache_replacement_policy\x18\x05 \x01(\x0e28.buildbarn.configuration.eviction.CacheReplacementPolicyR\x16cacheReplacementPolicy\"\xc2\x01\n" + - "\x1aTracingMethodConfiguration\x12P\n" + - "%attributes_from_first_request_message\x18\x01 \x03(\tR!attributesFromFirstRequestMessage\x12R\n" + - "&attributes_from_first_response_message\x18\x02 \x03(\tR\"attributesFromFirstResponseMessage\"\x85\x01\n" + + "\x18cache_replacement_policy\x18\x05 \x01(\x0e28.buildbarn.configuration.eviction.CacheReplacementPolicyR\x16cacheReplacementPolicy\"\xe8\x01\n" + + "\x1aTracingMethodConfiguration\x12E\n" + + "\x1fattributes_from_request_message\x18\x01 \x03(\tR\x1cattributesFromRequestMessage\x12G\n" + + " attributes_from_response_message\x18\x02 \x03(\tR\x1dattributesFromResponseMessage\x12:\n" + + "\x19include_followup_messages\x18\x03 \x01(\bR\x17includeFollowupMessages\"\x85\x01\n" + "\x18ServerRelayConfiguration\x12M\n" + "\bendpoint\x18\x01 \x01(\v21.buildbarn.configuration.grpc.ClientConfigurationR\bendpoint\x12\x1a\n" + "\bservices\x18\x02 \x03(\tR\bservicesB>Z