diff --git a/cloudmonitoring/doc.go b/cloudmonitoring/doc.go deleted file mode 100644 index 9437c753..00000000 --- a/cloudmonitoring/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package cloudmonitoring provides primitives for Cloud Monitoring integration. -package cloudmonitoring diff --git a/cloudmonitoring/metricmiddleware.go b/cloudmonitoring/metricmiddleware.go deleted file mode 100644 index d3f4501c..00000000 --- a/cloudmonitoring/metricmiddleware.go +++ /dev/null @@ -1,148 +0,0 @@ -package cloudmonitoring - -import ( - "context" - "fmt" - "strings" - "time" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// Metric names are based on OTEL semantic conventions for metrics. -// See: -// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics -const ( - clientRequestDurationMetricName = "rpc.client.duration" - - // there is no rpc_count equivalent int OTEL semantic conventions yet. - serverRequestCountMetricName = "rpc.server.rpc_count" - clientRequestCountMetricName = "rpc.client.rpc_count" -) - -func NewMetricMiddleware() (MetricMiddleware, error) { - meter := otel.GetMeterProvider().Meter("cloudrunner-go/cloudmonitoring") - - serverRequestCount, err := meter.Int64Counter( - serverRequestCountMetricName, - metric.WithUnit("1"), - metric.WithDescription("Count of RPCs received by a gRPC server."), - ) - if err != nil { - return MetricMiddleware{}, fmt.Errorf("create server request count counter: %w", err) - } - clientRequestCount, err := meter.Int64Counter( - clientRequestCountMetricName, - metric.WithUnit("1"), - metric.WithDescription("Count of RPCs sent by a gRPC client."), - ) - if err != nil { - return MetricMiddleware{}, fmt.Errorf("create client request count counter: %w", err) - } - clientRequestDuration, err := meter.Int64Histogram( - clientRequestDurationMetricName, - metric.WithUnit("ms"), - metric.WithDescription("Duration of RPCs sent by a gRPC client."), - ) - if err != nil { - return MetricMiddleware{}, fmt.Errorf("create client request duration histogram: %w", err) - } - return MetricMiddleware{ - serverRequestCount: serverRequestCount, - clientRequestCount: clientRequestCount, - clientRequestDuration: clientRequestDuration, - }, nil -} - -type MetricMiddleware struct { - serverRequestCount metric.Int64Counter - clientRequestCount metric.Int64Counter - clientRequestDuration metric.Int64Histogram -} - -// GRPCUnaryServerInterceptor implements grpc.UnaryServerInterceptor and -// emits metrics for request count and request duration when a gRPC server -// receives requests. -func (m *MetricMiddleware) GRPCUnaryServerInterceptor( - ctx context.Context, - request interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, -) (resp interface{}, err error) { - response, err := handler(ctx, request) - code := status.Code(err) - attrs := rpcAttrs(info.FullMethod, code) - m.serverRequestCount.Add(ctx, 1, attrs) - return response, err -} - -// GRPCStreamServerInterceptor implements grpc.UnaryServerInterceptor and -// emits metrics for request count and request duration when a gRPC server -// receives streaming requests. -func (m *MetricMiddleware) GRPCStreamServerInterceptor( - srv interface{}, - ss grpc.ServerStream, - info *grpc.StreamServerInfo, - handler grpc.StreamHandler, -) (err error) { - err = handler(srv, ss) - code := status.Code(err) - attrs := rpcAttrs(info.FullMethod, code) - m.serverRequestCount.Add(ss.Context(), 1, attrs) - return err -} - -// GRPCUnaryClientInterceptor provides request logging as a grpc.UnaryClientInterceptor. -func (m *MetricMiddleware) GRPCUnaryClientInterceptor( - ctx context.Context, - fullMethod string, - request interface{}, - response interface{}, - cc *grpc.ClientConn, - invoker grpc.UnaryInvoker, - opts ...grpc.CallOption, -) error { - startTime := time.Now() - err := invoker(ctx, fullMethod, request, response, cc, opts...) - code := status.Code(err) - duration := time.Since(startTime) - - attrs := rpcAttrs(fullMethod, code) - m.clientRequestCount.Add(ctx, 1, attrs) - m.clientRequestDuration.Record(ctx, duration.Milliseconds(), attrs) - return err -} - -func rpcAttrs(fullMethod string, code codes.Code) metric.MeasurementOption { - attrs := make([]attribute.KeyValue, 0, 5) - attrs = append( - attrs, - semconv.RPCSystemKey.String("grpc"), - semconv.RPCGRPCStatusCodeKey.Int64(int64(code)), - // Google Cloud Monitoring does not recognize semconv status code enum, - // so add an attributes with string representation of status code. - attribute.Stringer("rpc.grpc.code", code), - ) - if service, method, ok := splitFullMethod(fullMethod); ok { - attrs = append( - attrs, - semconv.RPCServiceKey.String(service), - semconv.RPCMethodKey.String(method), - ) - } - return metric.WithAttributes(attrs...) -} - -func splitFullMethod(fullMethod string) (service, method string, ok bool) { - serviceAndMethod := strings.SplitN(strings.TrimPrefix(fullMethod, "/"), "/", 2) - if len(serviceAndMethod) != 2 { - return "", "", false - } - return serviceAndMethod[0], serviceAndMethod[1], true -} diff --git a/dialservice.go b/dialservice.go index eff094fd..bfc9c517 100644 --- a/dialservice.go +++ b/dialservice.go @@ -20,11 +20,9 @@ func DialService(ctx context.Context, target string, opts ...grpc.DialOption) (* target, append( []grpc.DialOption{ + grpc.WithStatsHandler(otelgrpc.NewClientHandler()), grpc.WithDefaultServiceConfig(run.config.Client.AsServiceConfigJSON()), grpc.WithChainUnaryInterceptor( - //nolint:staticcheck // package is deprecated, replace when possible - otelgrpc.UnaryClientInterceptor(), - run.metricMiddleware.GRPCUnaryClientInterceptor, run.requestLoggerMiddleware.GRPCUnaryClientInterceptor, run.clientMiddleware.GRPCUnaryClientInterceptor, ), diff --git a/go.mod b/go.mod index e5d4b01e..d979de65 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ require ( go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel/bridge/opencensus v1.28.0 - go.opentelemetry.io/otel/metric v1.28.0 go.opentelemetry.io/otel/sdk v1.28.0 go.opentelemetry.io/otel/sdk/metric v1.28.0 go.uber.org/zap v1.27.0 @@ -64,6 +63,7 @@ require ( github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/crypto v0.25.0 // indirect diff --git a/grpcserver.go b/grpcserver.go index b541f18f..c19167bc 100644 --- a/grpcserver.go +++ b/grpcserver.go @@ -19,21 +19,16 @@ func NewGRPCServer(ctx context.Context, opts ...grpc.ServerOption) *grpc.Server panic("cloudrunner.NewGRPCServer: must be called with a context from cloudrunner.Run") } serverOptions := []grpc.ServerOption{ + grpc.StatsHandler(otelgrpc.NewServerHandler()), grpc.ChainUnaryInterceptor( - //nolint:staticcheck // package is deprecated, replace when possible - otelgrpc.UnaryServerInterceptor(), - run.loggerMiddleware.GRPCUnaryServerInterceptor, // adds context logger - run.traceMiddleware.GRPCServerUnaryInterceptor, // needs the context logger - run.metricMiddleware.GRPCUnaryServerInterceptor, + run.loggerMiddleware.GRPCUnaryServerInterceptor, // adds context logger + run.traceMiddleware.GRPCServerUnaryInterceptor, // needs the context logger run.requestLoggerMiddleware.GRPCUnaryServerInterceptor, // needs to run after trace run.serverMiddleware.GRPCUnaryServerInterceptor, // needs to run after request logger ), grpc.ChainStreamInterceptor( - //nolint:staticcheck // package is deprecated, replace when possible - otelgrpc.StreamServerInterceptor(), run.loggerMiddleware.GRPCStreamServerInterceptor, run.traceMiddleware.GRPCStreamServerInterceptor, - run.metricMiddleware.GRPCStreamServerInterceptor, run.requestLoggerMiddleware.GRPCStreamServerInterceptor, run.serverMiddleware.GRPCStreamServerInterceptor, ), diff --git a/run.go b/run.go index 09bc497d..97a03ee7 100644 --- a/run.go +++ b/run.go @@ -13,7 +13,6 @@ import ( "go.einride.tech/cloudrunner/cloudclient" "go.einride.tech/cloudrunner/cloudconfig" - "go.einride.tech/cloudrunner/cloudmonitoring" "go.einride.tech/cloudrunner/cloudotel" "go.einride.tech/cloudrunner/cloudprofiler" "go.einride.tech/cloudrunner/cloudrequestlog" @@ -101,10 +100,6 @@ func Run(fn func(context.Context) error, options ...Option) (err error) { if run.requestLoggerMiddleware.MessageTransformer == nil { run.requestLoggerMiddleware.MessageTransformer = protosensitive.Redact } - run.metricMiddleware, err = cloudmonitoring.NewMetricMiddleware() - if err != nil { - return fmt.Errorf("cloudrunner.Run: %w", err) - } ctx = withRunContext(ctx, &run) ctx = cloudruntime.WithConfig(ctx, run.config.Runtime) logger, err := cloudzap.NewLogger(run.config.Logger) @@ -182,7 +177,6 @@ type runContext struct { clientMiddleware cloudclient.Middleware requestLoggerMiddleware cloudrequestlog.Middleware traceMiddleware cloudtrace.Middleware - metricMiddleware cloudmonitoring.MetricMiddleware securityHeadersMiddleware cloudserver.SecurityHeadersMiddleware }