From 3203812df55ccceb4eff9e5984d325c111df42cc Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Sun, 3 Mar 2024 15:12:03 +0900 Subject: [PATCH 01/15] otelgrpc: Add filter for stats handler --- .../google.golang.org/grpc/otelgrpc/config.go | 41 +- .../grpc/otelgrpc/filters/filters.go | 47 +- .../grpc/otelgrpc/filters/filters_test.go | 234 +++------- .../otelgrpc/filters/interceptor/filters.go | 167 +++++++ .../filters/interceptor/filters_test.go | 430 ++++++++++++++++++ .../grpc/otelgrpc/interceptor.go | 8 +- .../grpc/otelgrpc/stats_handler.go | 12 + 7 files changed, 708 insertions(+), 231 deletions(-) create mode 100644 instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go create mode 100644 instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/instrumentation/google.golang.org/grpc/otelgrpc/config.go index 7a26fb5f6d2..80643ecb79b 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/config.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/config.go @@ -4,6 +4,8 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" import ( + "google.golang.org/grpc/stats" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -20,18 +22,24 @@ const ( GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) +// InterceptorFilter is a predicate used to determine whether a given request in +// interceptor info should be traced. A InterceptorFilter must return true if +// the request should be traced. +type InterceptorFilter func(*InterceptorInfo) bool + // Filter is a predicate used to determine whether a given request in // interceptor info should be traced. A Filter must return true if // the request should be traced. -type Filter func(*InterceptorInfo) bool +type Filter func(*stats.RPCTagInfo) bool // config is a group of options for this instrumentation. type config struct { - Filter Filter - Propagators propagation.TextMapPropagator - TracerProvider trace.TracerProvider - MeterProvider metric.MeterProvider - SpanStartOptions []trace.SpanStartOption + Filter Filter + InterceptorFilter InterceptorFilter + Propagators propagation.TextMapPropagator + TracerProvider trace.TracerProvider + MeterProvider metric.MeterProvider + SpanStartOptions []trace.SpanStartOption ReceivedEvent bool SentEvent bool @@ -150,17 +158,30 @@ func (o tracerProviderOption) apply(c *config) { } // WithInterceptorFilter returns an Option to use the request filter. -// -// Deprecated: Use stats handlers instead. -func WithInterceptorFilter(f Filter) Option { +func WithInterceptorFilter(f InterceptorFilter) Option { return interceptorFilterOption{f: f} } type interceptorFilterOption struct { - f Filter + f InterceptorFilter } func (o interceptorFilterOption) apply(c *config) { + if o.f != nil { + c.InterceptorFilter = o.f + } +} + +// WithFilter returns an Option to use the request filter. +func WithFilter(f Filter) Option { + return filterOption{f: f} +} + +type filterOption struct { + f Filter +} + +func (o filterOption) apply(c *config) { if o.f != nil { c.Filter = o.f } diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go index e86c0018292..26d703fbbe5 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go @@ -7,6 +7,8 @@ import ( "path" "strings" + "google.golang.org/grpc/stats" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" ) @@ -19,20 +21,8 @@ type gRPCPath struct { // and returns as gRPCPath object that has divided service and method names // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md // If name is not FullMethod, returned gRPCPath has empty service field. -func splitFullMethod(i *otelgrpc.InterceptorInfo) gRPCPath { - var name string - switch i.Type { - case otelgrpc.UnaryServer: - name = i.UnaryServerInfo.FullMethod - case otelgrpc.StreamServer: - name = i.StreamServerInfo.FullMethod - case otelgrpc.UnaryClient, otelgrpc.StreamClient: - name = i.Method - default: - name = i.Method - } - - s, m := path.Split(name) +func splitFullMethod(i *stats.RPCTagInfo) gRPCPath { + s, m := path.Split(i.FullMethodName) if s != "" { s = path.Clean(s) s = strings.TrimLeft(s, "/") @@ -47,7 +37,7 @@ func splitFullMethod(i *otelgrpc.InterceptorInfo) gRPCPath { // Any takes a list of Filters and returns a Filter that // returns true if any Filter in the list returns true. func Any(fs ...otelgrpc.Filter) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { for _, f := range fs { if f(i) { return true @@ -60,7 +50,7 @@ func Any(fs ...otelgrpc.Filter) otelgrpc.Filter { // All takes a list of Filters and returns a Filter that // returns true only if all Filters in the list return true. func All(fs ...otelgrpc.Filter) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { for _, f := range fs { if !f(i) { return false @@ -78,7 +68,7 @@ func None(fs ...otelgrpc.Filter) otelgrpc.Filter { // Not provides a convenience mechanism for inverting a Filter. func Not(f otelgrpc.Filter) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { return !f(i) } } @@ -86,7 +76,7 @@ func Not(f otelgrpc.Filter) otelgrpc.Filter { // MethodName returns a Filter that returns true if the request's // method name matches the provided string n. func MethodName(n string) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { p := splitFullMethod(i) return p.method == n } @@ -95,7 +85,7 @@ func MethodName(n string) otelgrpc.Filter { // MethodPrefix returns a Filter that returns true if the request's // method starts with the provided string pre. func MethodPrefix(pre string) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { p := splitFullMethod(i) return strings.HasPrefix(p.method, pre) } @@ -105,26 +95,15 @@ func MethodPrefix(pre string) otelgrpc.Filter { // full RPC method string, i.e. /package.service/method, starts with // the provided string n. func FullMethodName(n string) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { - var fm string - switch i.Type { - case otelgrpc.UnaryClient, otelgrpc.StreamClient: - fm = i.Method - case otelgrpc.UnaryServer: - fm = i.UnaryServerInfo.FullMethod - case otelgrpc.StreamServer: - fm = i.StreamServerInfo.FullMethod - default: - fm = i.Method - } - return fm == n + return func(i *stats.RPCTagInfo) bool { + return i.FullMethodName == n } } // ServiceName returns a Filter that returns true if the request's // service name, i.e. package.service, matches s. func ServiceName(s string) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { p := splitFullMethod(i) return p.service == s } @@ -133,7 +112,7 @@ func ServiceName(s string) otelgrpc.Filter { // ServicePrefix returns a Filter that returns true if the request's // service name, i.e. package.service, starts with the provided string pre. func ServicePrefix(pre string) otelgrpc.Filter { - return func(i *otelgrpc.InterceptorInfo) bool { + return func(i *stats.RPCTagInfo) bool { p := splitFullMethod(i) return strings.HasPrefix(p.service, pre) } diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters_test.go index 6ac330a68f0..46260b6585b 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters_test.go @@ -6,27 +6,21 @@ package filters // import "go.opentelemetry.io/contrib/instrumentation/google.go import ( "testing" - "google.golang.org/grpc" + "google.golang.org/grpc/stats" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" ) type testCase struct { name string - i *otelgrpc.InterceptorInfo + i *stats.RPCTagInfo f otelgrpc.Filter want bool } -func dummyUnaryServerInfo(n string) *grpc.UnaryServerInfo { - return &grpc.UnaryServerInfo{ - FullMethod: n, - } -} - -func dummyStreamServerInfo(n string) *grpc.StreamServerInfo { - return &grpc.StreamServerInfo{ - FullMethod: n, +func dummyRPCTagInfo(n string) *stats.RPCTagInfo { + return &stats.RPCTagInfo{ + FullMethodName: n, } } @@ -34,32 +28,14 @@ func TestMethodName(t *testing.T) { const dummyFullMethodName = "/example.HelloService/Hello" tcs := []testCase{ { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, - f: MethodName("Hello"), - want: true, - }, - { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, - f: MethodName("Hello"), - want: true, - }, - { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + name: "true", + i: dummyRPCTagInfo(dummyFullMethodName), f: MethodName("Hello"), want: true, }, { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, - f: MethodName("Hello"), - want: true, - }, - { - name: "unary client interceptor fail", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethodName), f: MethodName("Goodbye"), want: false, }, @@ -77,32 +53,14 @@ func TestMethodPrefix(t *testing.T) { const dummyFullMethodName = "/example.HelloService/FoobarHello" tcs := []testCase{ { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, - f: MethodPrefix("Foobar"), - want: true, - }, - { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, - f: MethodPrefix("Foobar"), - want: true, - }, - { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, - f: MethodPrefix("Foobar"), - want: true, - }, - { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + name: "true", + i: dummyRPCTagInfo(dummyFullMethodName), f: MethodPrefix("Foobar"), want: true, }, { - name: "unary client interceptor fail", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethodName), f: MethodPrefix("Barfoo"), want: false, }, @@ -119,32 +77,14 @@ func TestFullMethodName(t *testing.T) { const dummyFullMethodName = "/example.HelloService/Hello" tcs := []testCase{ { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, - f: FullMethodName(dummyFullMethodName), - want: true, - }, - { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, - f: FullMethodName(dummyFullMethodName), - want: true, - }, - { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, - f: FullMethodName(dummyFullMethodName), - want: true, - }, - { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + name: "true", + i: dummyRPCTagInfo(dummyFullMethodName), f: FullMethodName(dummyFullMethodName), want: true, }, { - name: "unary client interceptor fail", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethodName), f: FullMethodName("/example.HelloService/Goodbye"), want: false, }, @@ -163,32 +103,14 @@ func TestServiceName(t *testing.T) { tcs := []testCase{ { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true", + i: dummyRPCTagInfo(dummyFullMethodName), f: ServiceName("example.HelloService"), want: true, }, { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, - f: ServiceName("example.HelloService"), - want: true, - }, - { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, - f: ServiceName("example.HelloService"), - want: true, - }, - { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, - f: ServiceName("example.HelloService"), - want: true, - }, - { - name: "unary client interceptor fail", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethodName), f: ServiceName("opentelemetry.HelloService"), want: false, }, @@ -206,32 +128,14 @@ func TestServicePrefix(t *testing.T) { const dummyFullMethodName = "/example.HelloService/FoobarHello" tcs := []testCase{ { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, - f: ServicePrefix("example"), - want: true, - }, - { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + name: "true", + i: dummyRPCTagInfo(dummyFullMethodName), f: ServicePrefix("example"), want: true, }, { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, - f: ServicePrefix("example"), - want: true, - }, - { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, - f: ServicePrefix("example"), - want: true, - }, - { - name: "unary client interceptor fail", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethodName), f: ServicePrefix("opentelemetry"), want: false, }, @@ -248,20 +152,20 @@ func TestAny(t *testing.T) { const dummyFullMethodName = "/example.HelloService/FoobarHello" tcs := []testCase{ { - name: "unary client interceptor true && true", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true && true", + i: dummyRPCTagInfo(dummyFullMethodName), f: Any(MethodName("FoobarHello"), MethodPrefix("Foobar")), want: true, }, { - name: "unary client interceptor false && true", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false && true", + i: dummyRPCTagInfo(dummyFullMethodName), f: Any(MethodName("Hello"), MethodPrefix("Foobar")), want: true, }, { - name: "unary client interceptor false && false", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false && false", + i: dummyRPCTagInfo(dummyFullMethodName), f: Any(MethodName("Goodbye"), MethodPrefix("Barfoo")), want: false, }, @@ -278,20 +182,20 @@ func TestAll(t *testing.T) { const dummyFullMethodName = "/example.HelloService/FoobarHello" tcs := []testCase{ { - name: "unary client interceptor true && true", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true && true", + i: dummyRPCTagInfo(dummyFullMethodName), f: All(MethodName("FoobarHello"), MethodPrefix("Foobar")), want: true, }, { - name: "unary client interceptor true && false", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true && false", + i: dummyRPCTagInfo(dummyFullMethodName), f: All(MethodName("FoobarHello"), MethodPrefix("Barfoo")), want: false, }, { - name: "unary client interceptor false && false", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false && false", + i: dummyRPCTagInfo(dummyFullMethodName), f: All(MethodName("FoobarGoodbye"), MethodPrefix("Barfoo")), want: false, }, @@ -308,20 +212,20 @@ func TestNone(t *testing.T) { const dummyFullMethodName = "/example.HelloService/FoobarHello" tcs := []testCase{ { - name: "unary client interceptor true && true", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true && true", + i: dummyRPCTagInfo(dummyFullMethodName), f: None(MethodName("FoobarHello"), MethodPrefix("Foobar")), want: false, }, { - name: "unary client interceptor true && false", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "true && false", + i: dummyRPCTagInfo(dummyFullMethodName), f: None(MethodName("FoobarHello"), MethodPrefix("Barfoo")), want: false, }, { - name: "unary client interceptor false && false", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "false && false", + i: dummyRPCTagInfo(dummyFullMethodName), f: None(MethodName("FoobarGoodbye"), MethodPrefix("Barfoo")), want: true, }, @@ -339,19 +243,19 @@ func TestNot(t *testing.T) { tcs := []testCase{ { name: "methodname not", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + i: dummyRPCTagInfo(dummyFullMethodName), f: Not(MethodName("FoobarHello")), want: false, }, { name: "method prefix not", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + i: dummyRPCTagInfo(dummyFullMethodName), f: Not(MethodPrefix("FoobarHello")), want: false, }, { - name: "unary client interceptor not all(true && true)", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + name: "not all(true && true)", + i: dummyRPCTagInfo(dummyFullMethodName), f: Not(All(MethodName("FoobarHello"), MethodPrefix("Foobar"))), want: false, }, @@ -372,50 +276,14 @@ func TestHealthCheck(t *testing.T) { ) tcs := []testCase{ { - name: "unary client interceptor healthcheck", - i: &otelgrpc.InterceptorInfo{Method: healthCheck, Type: otelgrpc.UnaryClient}, + name: "true", + i: dummyRPCTagInfo(healthCheck), f: HealthCheck(), want: true, }, { - name: "stream client interceptor healthcheck", - i: &otelgrpc.InterceptorInfo{Method: healthCheck, Type: otelgrpc.StreamClient}, - f: HealthCheck(), - want: true, - }, - { - name: "unary server interceptor healthcheck", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(healthCheck), Type: otelgrpc.UnaryServer}, - f: HealthCheck(), - want: true, - }, - { - name: "stream server interceptor healthcheck", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(healthCheck), Type: otelgrpc.StreamServer}, - f: HealthCheck(), - want: true, - }, - { - name: "unary client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethod, Type: otelgrpc.UnaryClient}, - f: HealthCheck(), - want: false, - }, - { - name: "stream client interceptor", - i: &otelgrpc.InterceptorInfo{Method: dummyFullMethod, Type: otelgrpc.StreamClient}, - f: HealthCheck(), - want: false, - }, - { - name: "unary server interceptor", - i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethod), Type: otelgrpc.UnaryServer}, - f: HealthCheck(), - want: false, - }, - { - name: "stream server interceptor", - i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethod), Type: otelgrpc.StreamServer}, + name: "false", + i: dummyRPCTagInfo(dummyFullMethod), f: HealthCheck(), want: false, }, diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go new file mode 100644 index 00000000000..9dee9149dfb --- /dev/null +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go @@ -0,0 +1,167 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package interceptor // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor" + +import ( + "path" + "strings" + + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" +) + +type gRPCPath struct { + service string + method string +} + +// splitFullMethod splits path defined in gRPC protocol +// and returns as gRPCPath object that has divided service and method names +// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md +// If name is not FullMethod, returned gRPCPath has empty service field. +func splitFullMethod(i *otelgrpc.InterceptorInfo) gRPCPath { + var name string + switch i.Type { + case otelgrpc.UnaryServer: + name = i.UnaryServerInfo.FullMethod + case otelgrpc.StreamServer: + name = i.StreamServerInfo.FullMethod + case otelgrpc.UnaryClient, otelgrpc.StreamClient: + name = i.Method + default: + name = i.Method + } + + s, m := path.Split(name) + if s != "" { + s = path.Clean(s) + s = strings.TrimLeft(s, "/") + } + + return gRPCPath{ + service: s, + method: m, + } +} + +// Any takes a list of Filters and returns a Filter that +// returns true if any Filter in the list returns true. +// +// Deprecated: Use stats handler instead. +func Any(fs ...otelgrpc.InterceptorFilter) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + for _, f := range fs { + if f(i) { + return true + } + } + return false + } +} + +// All takes a list of Filters and returns a Filter that +// returns true only if all Filters in the list return true. +// +// Deprecated: Use stats handler instead. +func All(fs ...otelgrpc.InterceptorFilter) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + for _, f := range fs { + if !f(i) { + return false + } + } + return true + } +} + +// None takes a list of Filters and returns a Filter that returns +// true only if none of the Filters in the list return true. +// +// Deprecated: Use stats handler instead. +func None(fs ...otelgrpc.InterceptorFilter) otelgrpc.InterceptorFilter { + return Not(Any(fs...)) +} + +// Not provides a convenience mechanism for inverting a Filter. +// +// Deprecated: Use stats handler instead. +func Not(f otelgrpc.InterceptorFilter) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + return !f(i) + } +} + +// MethodName returns a Filter that returns true if the request's +// method name matches the provided string n. +// +// Deprecated: Use stats handler instead. +func MethodName(n string) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + p := splitFullMethod(i) + return p.method == n + } +} + +// MethodPrefix returns a Filter that returns true if the request's +// method starts with the provided string pre. +// +// Deprecated: Use stats handler instead. +func MethodPrefix(pre string) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + p := splitFullMethod(i) + return strings.HasPrefix(p.method, pre) + } +} + +// FullMethodName returns a Filter that returns true if the request's +// full RPC method string, i.e. /package.service/method, starts with +// the provided string n. +// +// Deprecated: Use stats handler instead. +func FullMethodName(n string) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + var fm string + switch i.Type { + case otelgrpc.UnaryClient, otelgrpc.StreamClient: + fm = i.Method + case otelgrpc.UnaryServer: + fm = i.UnaryServerInfo.FullMethod + case otelgrpc.StreamServer: + fm = i.StreamServerInfo.FullMethod + default: + fm = i.Method + } + return fm == n + } +} + +// ServiceName returns a Filter that returns true if the request's +// service name, i.e. package.service, matches s. +// +// Deprecated: Use stats handler instead. +func ServiceName(s string) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + p := splitFullMethod(i) + return p.service == s + } +} + +// ServicePrefix returns a Filter that returns true if the request's +// service name, i.e. package.service, starts with the provided string pre. +// +// Deprecated: Use stats handler instead. +func ServicePrefix(pre string) otelgrpc.InterceptorFilter { + return func(i *otelgrpc.InterceptorInfo) bool { + p := splitFullMethod(i) + return strings.HasPrefix(p.service, pre) + } +} + +// HealthCheck returns a Filter that returns true if the request's +// service name is health check defined by gRPC Health Checking Protocol. +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md +// +// Deprecated: Use stats handler instead. +func HealthCheck() otelgrpc.InterceptorFilter { + return ServicePrefix("grpc.health.v1.Health") +} diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go new file mode 100644 index 00000000000..23fb9db493a --- /dev/null +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go @@ -0,0 +1,430 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package interceptor // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor" + +import ( + "testing" + + "google.golang.org/grpc" + + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" +) + +type testCase struct { + name string + i *otelgrpc.InterceptorInfo + f otelgrpc.InterceptorFilter + want bool +} + +func dummyUnaryServerInfo(n string) *grpc.UnaryServerInfo { + return &grpc.UnaryServerInfo{ + FullMethod: n, + } +} + +func dummyStreamServerInfo(n string) *grpc.StreamServerInfo { + return &grpc.StreamServerInfo{ + FullMethod: n, + } +} + +func TestMethodName(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/Hello" + tcs := []testCase{ + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: MethodName("Hello"), + want: true, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + f: MethodName("Hello"), + want: true, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: MethodName("Hello"), + want: true, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + f: MethodName("Hello"), + want: true, + }, + { + name: "unary client interceptor fail", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: MethodName("Goodbye"), + want: false, + }, + } + + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestMethodPrefix(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: MethodPrefix("Foobar"), + want: true, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + f: MethodPrefix("Foobar"), + want: true, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: MethodPrefix("Foobar"), + want: true, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + f: MethodPrefix("Foobar"), + want: true, + }, + { + name: "unary client interceptor fail", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: MethodPrefix("Barfoo"), + want: false, + }, + } + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestFullMethodName(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/Hello" + tcs := []testCase{ + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: FullMethodName(dummyFullMethodName), + want: true, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + f: FullMethodName(dummyFullMethodName), + want: true, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: FullMethodName(dummyFullMethodName), + want: true, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + f: FullMethodName(dummyFullMethodName), + want: true, + }, + { + name: "unary client interceptor fail", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: FullMethodName("/example.HelloService/Goodbye"), + want: false, + }, + } + + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestServiceName(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/Hello" + + tcs := []testCase{ + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: ServiceName("example.HelloService"), + want: true, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + f: ServiceName("example.HelloService"), + want: true, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: ServiceName("example.HelloService"), + want: true, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + f: ServiceName("example.HelloService"), + want: true, + }, + { + name: "unary client interceptor fail", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: ServiceName("opentelemetry.HelloService"), + want: false, + }, + } + + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestServicePrefix(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: ServicePrefix("example"), + want: true, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.StreamClient}, + f: ServicePrefix("example"), + want: true, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: ServicePrefix("example"), + want: true, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethodName), Type: otelgrpc.StreamServer}, + f: ServicePrefix("example"), + want: true, + }, + { + name: "unary client interceptor fail", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: ServicePrefix("opentelemetry"), + want: false, + }, + } + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestAny(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "unary client interceptor true && true", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: Any(MethodName("FoobarHello"), MethodPrefix("Foobar")), + want: true, + }, + { + name: "unary client interceptor false && true", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: Any(MethodName("Hello"), MethodPrefix("Foobar")), + want: true, + }, + { + name: "unary client interceptor false && false", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: Any(MethodName("Goodbye"), MethodPrefix("Barfoo")), + want: false, + }, + } + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestAll(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "unary client interceptor true && true", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: All(MethodName("FoobarHello"), MethodPrefix("Foobar")), + want: true, + }, + { + name: "unary client interceptor true && false", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: All(MethodName("FoobarHello"), MethodPrefix("Barfoo")), + want: false, + }, + { + name: "unary client interceptor false && false", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: All(MethodName("FoobarGoodbye"), MethodPrefix("Barfoo")), + want: false, + }, + } + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestNone(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "unary client interceptor true && true", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: None(MethodName("FoobarHello"), MethodPrefix("Foobar")), + want: false, + }, + { + name: "unary client interceptor true && false", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: None(MethodName("FoobarHello"), MethodPrefix("Barfoo")), + want: false, + }, + { + name: "unary client interceptor false && false", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: None(MethodName("FoobarGoodbye"), MethodPrefix("Barfoo")), + want: true, + }, + } + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestNot(t *testing.T) { + const dummyFullMethodName = "/example.HelloService/FoobarHello" + tcs := []testCase{ + { + name: "methodname not", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: Not(MethodName("FoobarHello")), + want: false, + }, + { + name: "method prefix not", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethodName), Type: otelgrpc.UnaryServer}, + f: Not(MethodPrefix("FoobarHello")), + want: false, + }, + { + name: "unary client interceptor not all(true && true)", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethodName, Type: otelgrpc.UnaryClient}, + f: Not(All(MethodName("FoobarHello"), MethodPrefix("Foobar"))), + want: false, + }, + } + + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} + +func TestHealthCheck(t *testing.T) { + const ( + healthCheck = "/grpc.health.v1.Health/Check" + dummyFullMethod = "/example.HelloService/FoobarHello" + ) + tcs := []testCase{ + { + name: "unary client interceptor healthcheck", + i: &otelgrpc.InterceptorInfo{Method: healthCheck, Type: otelgrpc.UnaryClient}, + f: HealthCheck(), + want: true, + }, + { + name: "stream client interceptor healthcheck", + i: &otelgrpc.InterceptorInfo{Method: healthCheck, Type: otelgrpc.StreamClient}, + f: HealthCheck(), + want: true, + }, + { + name: "unary server interceptor healthcheck", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(healthCheck), Type: otelgrpc.UnaryServer}, + f: HealthCheck(), + want: true, + }, + { + name: "stream server interceptor healthcheck", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(healthCheck), Type: otelgrpc.StreamServer}, + f: HealthCheck(), + want: true, + }, + { + name: "unary client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethod, Type: otelgrpc.UnaryClient}, + f: HealthCheck(), + want: false, + }, + { + name: "stream client interceptor", + i: &otelgrpc.InterceptorInfo{Method: dummyFullMethod, Type: otelgrpc.StreamClient}, + f: HealthCheck(), + want: false, + }, + { + name: "unary server interceptor", + i: &otelgrpc.InterceptorInfo{UnaryServerInfo: dummyUnaryServerInfo(dummyFullMethod), Type: otelgrpc.UnaryServer}, + f: HealthCheck(), + want: false, + }, + { + name: "stream server interceptor", + i: &otelgrpc.InterceptorInfo{StreamServerInfo: dummyStreamServerInfo(dummyFullMethod), Type: otelgrpc.StreamServer}, + f: HealthCheck(), + want: false, + }, + } + + for _, tc := range tcs { + out := tc.f(tc.i) + if tc.want != out { + t.Errorf("test case '%v' failed, wanted %v but obtained %v", tc.name, tc.want, out) + } + } +} diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go b/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go index a4fa5357a56..8ce2eabbff7 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go @@ -70,7 +70,7 @@ func UnaryClientInterceptor(opts ...Option) grpc.UnaryClientInterceptor { Method: method, Type: UnaryClient, } - if cfg.Filter != nil && !cfg.Filter(i) { + if cfg.InterceptorFilter != nil && !cfg.InterceptorFilter(i) { return invoker(ctx, method, req, reply, cc, callOpts...) } @@ -230,7 +230,7 @@ func StreamClientInterceptor(opts ...Option) grpc.StreamClientInterceptor { Method: method, Type: StreamClient, } - if cfg.Filter != nil && !cfg.Filter(i) { + if cfg.InterceptorFilter != nil && !cfg.InterceptorFilter(i) { return streamer(ctx, desc, cc, method, callOpts...) } @@ -285,7 +285,7 @@ func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor { UnaryServerInfo: info, Type: UnaryServer, } - if cfg.Filter != nil && !cfg.Filter(i) { + if cfg.InterceptorFilter != nil && !cfg.InterceptorFilter(i) { return handler(ctx, req) } @@ -411,7 +411,7 @@ func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor { StreamServerInfo: info, Type: StreamServer, } - if cfg.Filter != nil && !cfg.Filter(i) { + if cfg.InterceptorFilter != nil && !cfg.InterceptorFilter(i) { return handler(srv, wrapServerStream(ctx, ss, cfg)) } diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index c1c99874696..69e846e9f5e 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -27,6 +27,7 @@ type gRPCContext struct { messagesReceived int64 messagesSent int64 metricAttrs []attribute.KeyValue + record bool } type serverHandler struct { @@ -66,6 +67,10 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont gctx := gRPCContext{ metricAttrs: attrs, + record: true, + } + if h.config.Filter != nil { + gctx.record = h.config.Filter(info) } return context.WithValue(ctx, gRPCContextKey{}, &gctx) } @@ -102,6 +107,10 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont gctx := gRPCContext{ metricAttrs: attrs, + record: true, + } + if h.config.Filter != nil { + gctx.record = h.config.Filter(info) } return inject(context.WithValue(ctx, gRPCContextKey{}, &gctx), h.config.Propagators) @@ -132,6 +141,9 @@ func (c *config) handleRPC(ctx context.Context, rs stats.RPCStats, isServer bool if gctx != nil { metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1) metricAttrs = append(metricAttrs, gctx.metricAttrs...) + if !gctx.record { + return + } } switch rs := rs.(type) { From 93304eee85f4fada1971fdaa90e47c45abf31106 Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Sun, 3 Mar 2024 17:04:57 +0900 Subject: [PATCH 02/15] Add entries to CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 479077cfbde..0f616868b01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) +- Add gRPC trace `Filter` for stats handler to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#5196) + +### Changed + +- The gRPC trace `Filter` for interceptor is moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/interceptor`. (#5196) ### Removed From 6da878b232d352fed2ac97956083ebda0938ab9d Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Tue, 5 Mar 2024 11:17:18 +0900 Subject: [PATCH 03/15] fix CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f616868b01..5764a2300fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed -- The gRPC trace `Filter` for interceptor is moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/interceptor`. (#5196) +- The gRPC trace `Filter` for interceptor is renamed to `InterceptorFilter`. (#5196) +- The gRPC trace filter functions `Any`, `All`, `None`, `Not`, `MethodName`, `MethodPrefix`, `FullMethodName`, `ServiceName`, `ServicePrefix` and `HealthCheck` for interceptor are moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor`. (#5196) ### Removed From a67ce81c6358fd11d1a2a6488c68ac0511d99a6b Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Wed, 6 Mar 2024 18:32:35 +0900 Subject: [PATCH 04/15] keep Deprecated comment of `WithInterceptorFilter` --- instrumentation/google.golang.org/grpc/otelgrpc/config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/instrumentation/google.golang.org/grpc/otelgrpc/config.go index 80643ecb79b..997d673f4bb 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/config.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/config.go @@ -158,6 +158,8 @@ func (o tracerProviderOption) apply(c *config) { } // WithInterceptorFilter returns an Option to use the request filter. +// +// Deprecated: Use stats handlers instead. func WithInterceptorFilter(f InterceptorFilter) Option { return interceptorFilterOption{f: f} } From bdd913c8d2a36e8ad441b7f338e39aeca03200aa Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Wed, 6 Mar 2024 18:47:51 +0900 Subject: [PATCH 05/15] deprecate `InterceptorFilter` type --- CHANGELOG.md | 4 ++++ instrumentation/google.golang.org/grpc/otelgrpc/config.go | 2 ++ .../grpc/otelgrpc/filters/interceptor/filters_test.go | 1 + 3 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5764a2300fa..bdfd6468971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The gRPC trace `Filter` for interceptor is renamed to `InterceptorFilter`. (#5196) - The gRPC trace filter functions `Any`, `All`, `None`, `Not`, `MethodName`, `MethodPrefix`, `FullMethodName`, `ServiceName`, `ServicePrefix` and `HealthCheck` for interceptor are moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor`. (#5196) +### Deprecated + +- The `InterceptorFilter` type in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` is deprecated. (#5196) + ### Removed - Drop support for [Go 1.20]. (#5163) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/instrumentation/google.golang.org/grpc/otelgrpc/config.go index 997d673f4bb..c9c96d382f6 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/config.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/config.go @@ -25,6 +25,8 @@ const ( // InterceptorFilter is a predicate used to determine whether a given request in // interceptor info should be traced. A InterceptorFilter must return true if // the request should be traced. +// +// Deprecated: Use stats handlers instead. type InterceptorFilter func(*InterceptorInfo) bool // Filter is a predicate used to determine whether a given request in diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go index 23fb9db493a..e2816cd7b64 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters_test.go @@ -14,6 +14,7 @@ import ( type testCase struct { name string i *otelgrpc.InterceptorInfo + //nolint:staticcheck // Interceptors are deprecated and will be removed in the next release. f otelgrpc.InterceptorFilter want bool } From 24f7819a914cc754de75de89578588c6f71f84cb Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Tue, 7 May 2024 17:01:30 +0900 Subject: [PATCH 06/15] change the location of `gctx.record` nil check location to reduce heap allocations --- .../google.golang.org/grpc/otelgrpc/stats_handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index 69e846e9f5e..fad58733fec 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -139,11 +139,11 @@ func (c *config) handleRPC(ctx context.Context, rs stats.RPCStats, isServer bool gctx, _ := ctx.Value(gRPCContextKey{}).(*gRPCContext) if gctx != nil { - metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1) - metricAttrs = append(metricAttrs, gctx.metricAttrs...) if !gctx.record { return } + metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1) + metricAttrs = append(metricAttrs, gctx.metricAttrs...) } switch rs := rs.(type) { From c5175084cfae28588cc02c8e6bf9a778005ff163 Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Tue, 7 May 2024 17:28:00 +0900 Subject: [PATCH 07/15] add documentation for filters and interceptor packages --- .../google.golang.org/grpc/otelgrpc/filters/filters.go | 2 ++ .../grpc/otelgrpc/filters/interceptor/filters.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go index 26d703fbbe5..770c049a3b6 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +// Package filters provides a set of filters useful with the +// otelgrpc.WithFilter() option to control which inbound requests are traced. package filters // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters" import ( diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go index 9dee9149dfb..a591a98653e 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go @@ -1,6 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +// Package interceptor provides a set of filters useful with the +// otelgrpc.WithInterceptorFilter() option to control which inbound requests are traced. +// +// Deprecated: Use filters package and otelgrpc.WithFilter() instead. package interceptor // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor" import ( From 733f3be58486734542d38bc5612004c598dd1764 Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Wed, 8 May 2024 09:34:19 +0900 Subject: [PATCH 08/15] fix the location of entries in CHANGELOG --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c734eac1b5..565f6e9430e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) +- Add an experimental `OTEL_METRICS_PRODUCERS` environment variable to `go.opentelemetry.io/contrib/autoexport` to be set metrics producers. (#5281) + - `prometheus` and `none` are supported values. You can specify multiple producers separated by a comma. + - Add `WithFallbackMetricProducer` option that adds a fallback if the `OTEL_METRICS_PRODUCERS` is not set or empty. +- The `go.opentelemetry.io/contrib/processors/baggage/baggagetrace` module. This module provides a Baggage Span Processor. (#5404) - Add gRPC trace `Filter` for stats handler to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#5196) ### Changed @@ -20,10 +24,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Deprecated -- Add an experimental `OTEL_METRICS_PRODUCERS` environment variable to `go.opentelemetry.io/contrib/autoexport` to be set metrics producers. (#5281) - - `prometheus` and `none` are supported values. You can specify multiple producers separated by a comma. - - Add `WithFallbackMetricProducer` option that adds a fallback if the `OTEL_METRICS_PRODUCERS` is not set or empty. -- The `go.opentelemetry.io/contrib/processors/baggage/baggagetrace` module. This module provides a Baggage Span Processor. (#5404) - The `InterceptorFilter` type in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` is deprecated. (#5196) ## [1.26.0/0.51.0/0.20.0/0.6.0/0.1.0] - 2024-04-24 From 02f36b34c755279b2445f72ef50632b549630f1e Mon Sep 17 00:00:00 2001 From: "Y.Matsuda" <44557218+ymtdzzz@users.noreply.github.com> Date: Wed, 8 May 2024 19:34:12 +0900 Subject: [PATCH 09/15] more reasonable doc comment for interceptor package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- .../grpc/otelgrpc/filters/interceptor/filters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go index a591a98653e..e13f09328a6 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Package interceptor provides a set of filters useful with the -// otelgrpc.WithInterceptorFilter() option to control which inbound requests are traced. +// [otelgrpc.WithInterceptorFilter] option to control which inbound requests are instrumented. // // Deprecated: Use filters package and otelgrpc.WithFilter() instead. package interceptor // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor" From e523a2da7087c0efad1aade7e8397c8275416106 Mon Sep 17 00:00:00 2001 From: "Y.Matsuda" <44557218+ymtdzzz@users.noreply.github.com> Date: Wed, 8 May 2024 19:37:09 +0900 Subject: [PATCH 10/15] more reasonable doc comment for interceptor package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- .../grpc/otelgrpc/filters/interceptor/filters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go index e13f09328a6..ca9cc9a305d 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor/filters.go @@ -4,7 +4,7 @@ // Package interceptor provides a set of filters useful with the // [otelgrpc.WithInterceptorFilter] option to control which inbound requests are instrumented. // -// Deprecated: Use filters package and otelgrpc.WithFilter() instead. +// Deprecated: Use filters package and [otelgrpc.WithFilter] instead. package interceptor // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor" import ( From 28f6c1cd2e8ea31ccd004fab1460ddd5980a22c0 Mon Sep 17 00:00:00 2001 From: "Y.Matsuda" <44557218+ymtdzzz@users.noreply.github.com> Date: Wed, 8 May 2024 19:39:32 +0900 Subject: [PATCH 11/15] Use more appropriate word in the comment of InterceptorFilter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- instrumentation/google.golang.org/grpc/otelgrpc/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/instrumentation/google.golang.org/grpc/otelgrpc/config.go index c9c96d382f6..8570fe5ee6a 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/config.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/config.go @@ -23,7 +23,7 @@ const ( ) // InterceptorFilter is a predicate used to determine whether a given request in -// interceptor info should be traced. A InterceptorFilter must return true if +// interceptor info should be instrumented. A InterceptorFilter must return true if // the request should be traced. // // Deprecated: Use stats handlers instead. From a7796404630f84422cc9a4e26ee5835159853ca6 Mon Sep 17 00:00:00 2001 From: "Y.Matsuda" <44557218+ymtdzzz@users.noreply.github.com> Date: Wed, 8 May 2024 19:40:11 +0900 Subject: [PATCH 12/15] more reasonable doc comment for filters package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robert Pająk --- .../google.golang.org/grpc/otelgrpc/filters/filters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go index 770c049a3b6..ee97b4175af 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Package filters provides a set of filters useful with the -// otelgrpc.WithFilter() option to control which inbound requests are traced. +// [otelgrpc.WithFilter] option to control which inbound requests are instrumented. package filters // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters" import ( From ebabbaaa40c6b4f23b722299bf9bbf5fd9e8aafc Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Wed, 8 May 2024 19:52:05 +0900 Subject: [PATCH 13/15] fix the comment of `Filter` --- instrumentation/google.golang.org/grpc/otelgrpc/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/instrumentation/google.golang.org/grpc/otelgrpc/config.go index 8570fe5ee6a..a199b36b4fa 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/config.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/config.go @@ -30,8 +30,8 @@ const ( type InterceptorFilter func(*InterceptorInfo) bool // Filter is a predicate used to determine whether a given request in -// interceptor info should be traced. A Filter must return true if -// the request should be traced. +// should be instrumented by the attatched RPC tag info. +// A Filter must return true if the request should be instrumented. type Filter func(*stats.RPCTagInfo) bool // config is a group of options for this instrumentation. From 28a27de4a4d6fcd76f2ac2f051f93e66e28294ec Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Wed, 8 May 2024 21:38:07 +0900 Subject: [PATCH 14/15] Clearly describe the effects of the change --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 565f6e9430e..5469be1a21c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - The gRPC trace `Filter` for interceptor is renamed to `InterceptorFilter`. (#5196) -- The gRPC trace filter functions `Any`, `All`, `None`, `Not`, `MethodName`, `MethodPrefix`, `FullMethodName`, `ServiceName`, `ServicePrefix` and `HealthCheck` for interceptor are moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor`. (#5196) +- The gRPC trace filter functions `Any`, `All`, `None`, `Not`, `MethodName`, `MethodPrefix`, `FullMethodName`, `ServiceName`, `ServicePrefix` and `HealthCheck` for interceptor are moved to `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters/interceptor`. + With this change, the filters in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` are now working for stats handler. (#5196) ### Deprecated From d22a1c98f14feba9d7523142c7dc74e22d590c86 Mon Sep 17 00:00:00 2001 From: ymtdzzz Date: Thu, 9 May 2024 16:41:33 +0900 Subject: [PATCH 15/15] add tests for `stats_handler.go` --- .../otelgrpc/test/grpc_stats_handler_test.go | 130 ++++++++++++------ 1 file changed, 89 insertions(+), 41 deletions(-) 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 f34ebdd6e15..72f9de68758 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 @@ -18,6 +18,7 @@ import ( "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" @@ -33,53 +34,100 @@ import ( ) func TestStatsHandler(t *testing.T) { - clientSR := tracetest.NewSpanRecorder() - clientTP := trace.NewTracerProvider(trace.WithSpanProcessor(clientSR)) - clientMetricReader := metric.NewManualReader() - clientMP := metric.NewMeterProvider(metric.WithReader(clientMetricReader)) - - serverSR := tracetest.NewSpanRecorder() - serverTP := trace.NewTracerProvider(trace.WithSpanProcessor(serverSR)) - serverMetricReader := metric.NewManualReader() - serverMP := metric.NewMeterProvider(metric.WithReader(serverMetricReader)) - - listener, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err, "failed to open port") - client := newGrpcTest(t, listener, - []grpc.DialOption{ - grpc.WithStatsHandler(otelgrpc.NewClientHandler( - otelgrpc.WithTracerProvider(clientTP), - otelgrpc.WithMeterProvider(clientMP), - otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents)), - ), + tests := []struct { + name string + filterSvcName string + expectRecorded bool + }{ + { + name: "Recorded", + filterSvcName: "grpc.testing.TestService", + expectRecorded: true, }, - []grpc.ServerOption{ - grpc.StatsHandler(otelgrpc.NewServerHandler( - otelgrpc.WithTracerProvider(serverTP), - otelgrpc.WithMeterProvider(serverMP), - otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents)), - ), + { + name: "Dropped", + filterSvcName: "grpc.testing.OtherService", + expectRecorded: false, }, - ) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - doCalls(ctx, client) + } - t.Run("ClientSpans", func(t *testing.T) { - checkClientSpans(t, clientSR.Ended(), listener.Addr().String()) - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clientSR := tracetest.NewSpanRecorder() + clientTP := trace.NewTracerProvider(trace.WithSpanProcessor(clientSR)) + clientMetricReader := metric.NewManualReader() + clientMP := metric.NewMeterProvider(metric.WithReader(clientMetricReader)) - t.Run("ClientMetrics", func(t *testing.T) { - checkClientMetrics(t, clientMetricReader) - }) + serverSR := tracetest.NewSpanRecorder() + serverTP := trace.NewTracerProvider(trace.WithSpanProcessor(serverSR)) + serverMetricReader := metric.NewManualReader() + serverMP := metric.NewMeterProvider(metric.WithReader(serverMetricReader)) - t.Run("ServerSpans", func(t *testing.T) { - checkServerSpans(t, serverSR.Ended()) - }) + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err, "failed to open port") + client := newGrpcTest(t, listener, + []grpc.DialOption{ + grpc.WithStatsHandler(otelgrpc.NewClientHandler( + otelgrpc.WithTracerProvider(clientTP), + otelgrpc.WithMeterProvider(clientMP), + otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents), + otelgrpc.WithFilter(filters.ServiceName(tt.filterSvcName))), + ), + }, + []grpc.ServerOption{ + grpc.StatsHandler(otelgrpc.NewServerHandler( + otelgrpc.WithTracerProvider(serverTP), + otelgrpc.WithMeterProvider(serverMP), + otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents), + otelgrpc.WithFilter(filters.ServiceName(tt.filterSvcName))), + ), + }, + ) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + doCalls(ctx, client) + + if tt.expectRecorded { + t.Run("ClientSpans", func(t *testing.T) { + checkClientSpans(t, clientSR.Ended(), listener.Addr().String()) + }) + + t.Run("ClientMetrics", func(t *testing.T) { + checkClientMetrics(t, clientMetricReader) + }) + + t.Run("ServerSpans", func(t *testing.T) { + checkServerSpans(t, serverSR.Ended()) + }) - t.Run("ServerMetrics", func(t *testing.T) { - checkServerMetrics(t, serverMetricReader) - }) + t.Run("ServerMetrics", func(t *testing.T) { + checkServerMetrics(t, serverMetricReader) + }) + } else { + t.Run("ClientSpans", func(t *testing.T) { + require.Len(t, clientSR.Ended(), 0) + }) + + t.Run("ClientMetrics", func(t *testing.T) { + rm := metricdata.ResourceMetrics{} + err := clientMetricReader.Collect(context.Background(), &rm) + assert.NoError(t, err) + require.Len(t, rm.ScopeMetrics, 0) + }) + + t.Run("ServerSpans", func(t *testing.T) { + require.Len(t, serverSR.Ended(), 0) + }) + + t.Run("ServerMetrics", func(t *testing.T) { + rm := metricdata.ResourceMetrics{} + err := serverMetricReader.Collect(context.Background(), &rm) + assert.NoError(t, err) + require.Len(t, rm.ScopeMetrics, 0) + }) + } + }) + } } func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan, addr string) {