From 737f7009815d61ebc5956e293942f545e97df9c4 Mon Sep 17 00:00:00 2001 From: Chris Reeves Date: Tue, 29 Oct 2024 09:26:35 +0000 Subject: [PATCH] feat(otelgrpc): support adding custom metric attributes to gRPCContext --- .../grpc/otelgrpc/internal/test/test_utils.go | 7 ++- .../grpc/otelgrpc/stats_handler.go | 11 ++++ .../otelgrpc/test/grpc_stats_handler_test.go | 50 +++++++++++++------ .../grpc/otelgrpc/test/stats_handler_test.go | 7 +++ 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/internal/test/test_utils.go b/instrumentation/google.golang.org/grpc/otelgrpc/internal/test/test_utils.go index 717db324b72..94597ad3a12 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/internal/test/test_utils.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/internal/test/test_utils.go @@ -36,11 +36,14 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + testpb "google.golang.org/grpc/interop/grpc_testing" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" - testpb "google.golang.org/grpc/interop/grpc_testing" + "go.opentelemetry.io/otel/attribute" + + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" ) var ( @@ -241,6 +244,7 @@ func NewTestServer() testpb.TestServiceServer { } func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { + otelgrpc.AddMetricAttributes(ctx, attribute.String("custom_test_metric", "OK")) return new(testpb.Empty), nil } @@ -261,6 +265,7 @@ func serverNewPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) } func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { + otelgrpc.AddMetricAttributes(ctx, attribute.String("custom_test_metric", "OK")) st := in.GetResponseStatus() if md, ok := metadata.FromIncomingContext(ctx); ok { if initialMetadata, ok := md[initialMetadataKey]; ok { diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index c01cb897cd3..7d0348cb4fd 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -31,6 +31,17 @@ type gRPCContext struct { record bool } +// AddMetricAttributes adds extra metric attributes which be added to recorded metrics. +// This can be called from within your gRPC handlers. +func AddMetricAttributes(ctx context.Context, attributes ...attribute.KeyValue) { + gctx, ok := ctx.Value(gRPCContextKey{}).(*gRPCContext) + if !ok { + return + } + + gctx.metricAttrs = append(gctx.metricAttrs, attributes...) +} + type serverHandler struct { *config } diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go index 884c60c1c21..dbf84b45bff 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go @@ -19,6 +19,9 @@ import ( testpb "google.golang.org/grpc/interop/grpc_testing" "google.golang.org/grpc/status" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/test" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric" @@ -27,15 +30,12 @@ import ( "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" - - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/test" ) var ( - testSpanAttr = attribute.String("test_span", "OK") - testMetricAttr = attribute.String("test_metric", "OK") + testSpanAttr = attribute.String("test_span", "OK") + testMetricAttr = attribute.String("test_metric", "OK") + customTestMetricAttr = attribute.String("custom_test_metric", "OK") ) func TestStatsHandler(t *testing.T) { @@ -1115,7 +1115,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("EmptyCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), }, { Attributes: attribute.NewSet( @@ -1123,7 +1125,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("UnaryCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), }, { Attributes: attribute.NewSet( @@ -1164,7 +1168,8 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("EmptyCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(0)), @@ -1177,7 +1182,8 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("UnaryCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Max: metricdata.NewExtrema(int64(271840)), @@ -1239,7 +1245,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("EmptyCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(0)), @@ -1252,7 +1260,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("UnaryCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Max: metricdata.NewExtrema(int64(314167)), @@ -1315,7 +1325,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("EmptyCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(1)), @@ -1329,7 +1341,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("UnaryCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(1)), @@ -1395,7 +1409,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("EmptyCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(1)), @@ -1409,7 +1425,9 @@ func checkServerMetrics(t *testing.T, reader metric.Reader) { semconv.RPCMethod("UnaryCall"), semconv.RPCService("grpc.testing.TestService"), semconv.RPCSystemGRPC, - testMetricAttr), + testMetricAttr, + customTestMetricAttr, + ), Bounds: []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}, BucketCounts: []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Max: metricdata.NewExtrema(int64(1)), diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/test/stats_handler_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/test/stats_handler_test.go index 1f96bbd4f8e..d00df99f5c2 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/test/stats_handler_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/test/stats_handler_test.go @@ -43,11 +43,15 @@ func TestStatsHandlerHandleRPCServerErrors(t *testing.T) { serviceName := "TestGrpcService" methodName := serviceName + "/" + name fullMethodName := "/" + methodName + // call the server handler ctx := serverHandler.TagRPC(context.Background(), &stats.RPCTagInfo{ FullMethodName: fullMethodName, }) + // add custom metric attribute + otelgrpc.AddMetricAttributes(ctx, customTestMetricAttr) + grpcErr := status.Error(check.grpcCode, check.grpcCode.String()) serverHandler.HandleRPC(ctx, &stats.End{ Error: grpcErr, @@ -82,6 +86,7 @@ func assertStatsHandlerServerMetrics(t *testing.T, reader metric.Reader, service otelgrpc.RPCSystemGRPC, otelgrpc.GRPCStatusCodeKey.Int64(int64(code)), testMetricAttr, + customTestMetricAttr, ), }, }, @@ -101,6 +106,7 @@ func assertStatsHandlerServerMetrics(t *testing.T, reader metric.Reader, service otelgrpc.RPCSystemGRPC, otelgrpc.GRPCStatusCodeKey.Int64(int64(code)), testMetricAttr, + customTestMetricAttr, ), }, }, @@ -120,6 +126,7 @@ func assertStatsHandlerServerMetrics(t *testing.T, reader metric.Reader, service otelgrpc.RPCSystemGRPC, otelgrpc.GRPCStatusCodeKey.Int64(int64(code)), testMetricAttr, + customTestMetricAttr, ), }, },