diff --git a/internal/daemon/metric/initialize_metrics.go b/internal/daemon/metric/initialize_metrics.go index 990f81d119..177988f855 100644 --- a/internal/daemon/metric/initialize_metrics.go +++ b/internal/daemon/metric/initialize_metrics.go @@ -33,7 +33,10 @@ var ( /* The following methods are used to initialize Prometheus histogram vectors for gRPC connections. */ -func rangeProtoFiles(m map[string][]string, fd protoreflect.FileDescriptor) bool { +// rangeProtoFiles returns true while there are services with associated methods in the proto package. +// It relies on RangeFilesByPackage to range through the package, and it adds them into map m. +// Services and methods for which filter() returns true are not added into the map. +func rangeProtoFiles(m map[string][]string, fd protoreflect.FileDescriptor, filter func(string, string) bool) bool { if fd.Services().Len() == 0 { return true } @@ -44,11 +47,18 @@ func rangeProtoFiles(m map[string][]string, fd protoreflect.FileDescriptor) bool continue } + serviceName := string(s.FullName()) methods := []string{} for j := 0; j < s.Methods().Len(); j++ { - methods = append(methods, string(s.Methods().Get(j).Name())) + methodName := string(s.Methods().Get(j).Name()) + if filter(serviceName, methodName) { + continue + } + methods = append(methods, methodName) + } + if len(methods) > 0 { + m[serviceName] = methods } - m[string(s.FullName())] = methods } return true @@ -56,27 +66,32 @@ func rangeProtoFiles(m map[string][]string, fd protoreflect.FileDescriptor) bool // appendServicesAndMethods ranges through all registered files in a specified proto package // and appends service and method names to the provided map m. -// This method is exported for testing purposes. -func appendServicesAndMethods(m map[string][]string, pkg protoreflect.FileDescriptor) { +func appendServicesAndMethods(m map[string][]string, pkg protoreflect.FileDescriptor, filter func(string, string) bool) { protoregistry.GlobalFiles.RangeFilesByPackage( pkg.Package(), - func(fd protoreflect.FileDescriptor) bool { return rangeProtoFiles(m, fd) }, + func(fd protoreflect.FileDescriptor) bool { return rangeProtoFiles(m, fd, filter) }, ) } // InitializeGrpcCollectorsFromPackage registers and zeroes a Prometheus // histogram, populating all service and method labels by ranging through // the package containing the provided FileDescriptor. +// The filter function takes in a service name and method name and skips adding them as labels +// upon returning true. // Note: inputting a protoreflect.FileDescriptor will populate all services and methods // found in its package, not just methods associated with that specific FileDescriptor. -func InitializeGrpcCollectorsFromPackage(r prometheus.Registerer, v prometheus.ObserverVec, pkg protoreflect.FileDescriptor, codes []codes.Code) { +func InitializeGrpcCollectorsFromPackage(r prometheus.Registerer, v prometheus.ObserverVec, + pkgs []protoreflect.FileDescriptor, codes []codes.Code, filter func(string, string) bool, +) { if r == nil { return } r.MustRegister(v) serviceNamesToMethodNames := make(map[string][]string, 0) - appendServicesAndMethods(serviceNamesToMethodNames, pkg) + for _, p := range pkgs { + appendServicesAndMethods(serviceNamesToMethodNames, p, filter) + } for serviceName, serviceMethods := range serviceNamesToMethodNames { for _, sm := range serviceMethods { diff --git a/internal/daemon/metric/initialize_metrics_test.go b/internal/daemon/metric/initialize_metrics_test.go index a891f7fa36..c4ddd33394 100644 --- a/internal/daemon/metric/initialize_metrics_test.go +++ b/internal/daemon/metric/initialize_metrics_test.go @@ -16,17 +16,35 @@ func Test_AppendServicesAndMethods(t *testing.T) { cases := []struct { name string pkg protoreflect.FileDescriptor + filter func(string, string) bool expected map[string][]string }{ { name: "basic", pkg: protooptions.File_testing_options_v1_service_proto, + filter: func(string, string) bool { return false }, + expected: map[string][]string{"testing.options.v1.TestService": {"TestMethod"}}, + }, + { + name: "filter-out", + pkg: protooptions.File_testing_options_v1_service_proto, + filter: func(_ string, m string) bool { + return m == "TestMethod" + }, + expected: map[string][]string{}, + }, + { + name: "filter-allow", + pkg: protooptions.File_testing_options_v1_service_proto, + filter: func(_ string, m string) bool { + return m != "TestMethod" + }, expected: map[string][]string{"testing.options.v1.TestService": {"TestMethod"}}, }, } for _, tc := range cases { m := make(map[string][]string, 0) - appendServicesAndMethods(m, tc.pkg) + appendServicesAndMethods(m, tc.pkg, tc.filter) assert.Equal(t, tc.expected, m) } } diff --git a/internal/daemon/worker/internal/metric/cluster_client.go b/internal/daemon/worker/internal/metric/cluster_client.go index ee701821a8..392819b1d2 100644 --- a/internal/daemon/worker/internal/metric/cluster_client.go +++ b/internal/daemon/worker/internal/metric/cluster_client.go @@ -6,16 +6,25 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/daemon/metric" - "github.com/hashicorp/boundary/internal/gen/controller/servers/services" + cservices "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/prometheus/client_golang/prometheus" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/reflect/protoreflect" ) const ( clusterClientSubsystem = "cluster_client" ) +// grpcCollectorFilter is currently used to filter the services and methods of the +// controller.servers.services package upon running InitializeClusterClientCollectors. +var grpcCollectorFilter = noopFilter + +func noopFilter(serviceName string, methodName string) bool { + return false +} + // grpcRequestLatency collects measurements of how long a gRPC // request between a cluster and its clients takes. var grpcRequestLatency prometheus.ObserverVec = prometheus.NewHistogramVec( @@ -37,8 +46,7 @@ type requestRecorder struct { start time.Time } -// NewRequestRecorder creates a requestRecorder struct which is used to measure gRPC client request latencies. -// For testing purposes, this method is exported. +// newRequestRecorder creates a requestRecorder struct which is used to measure gRPC client request latencies. func newRequestRecorder(fullMethodName string, reqLatency prometheus.ObserverVec) requestRecorder { service, method := metric.SplitMethodName(fullMethodName) r := requestRecorder{ @@ -82,5 +90,8 @@ func InstrumentClusterClient() grpc.UnaryClientInterceptor { // prometheus register and initializes them to 0 for all possible label // combinations. func InitializeClusterClientCollectors(r prometheus.Registerer) { - metric.InitializeGrpcCollectorsFromPackage(r, grpcRequestLatency, services.File_controller_servers_services_v1_session_service_proto, expectedGrpcClientCodes) + metric.InitializeGrpcCollectorsFromPackage(r, grpcRequestLatency, + []protoreflect.FileDescriptor{ + cservices.File_controller_servers_services_v1_session_service_proto, + }, expectedGrpcClientCodes, grpcCollectorFilter) }