Skip to content

Commit

Permalink
otelgrpc: Add Filter for stats handler (#5196)
Browse files Browse the repository at this point in the history
* otelgrpc: Add filter for stats handler

* Add entries to CHANGELOG

* fix CHANGELOG

* keep Deprecated comment of `WithInterceptorFilter`

* deprecate `InterceptorFilter` type

* change the location of `gctx.record` nil check location to reduce heap allocations

* add documentation for filters and interceptor packages

* fix the location of entries in CHANGELOG

* more reasonable doc comment for interceptor package

Co-authored-by: Robert Pająk <[email protected]>

* more reasonable doc comment for interceptor package

Co-authored-by: Robert Pająk <[email protected]>

* Use more appropriate word in the comment of InterceptorFilter

Co-authored-by: Robert Pająk <[email protected]>

* more reasonable doc comment for filters package

Co-authored-by: Robert Pająk <[email protected]>

* fix the comment of `Filter`

* Clearly describe the effects of the change

* add tests for `stats_handler.go`

---------

Co-authored-by: David Ashpole <[email protected]>
Co-authored-by: Robert Pająk <[email protected]>
Co-authored-by: Tyler Yahn <[email protected]>
  • Loading branch information
4 people authored May 9, 2024
1 parent d6e2fc4 commit 0483033
Show file tree
Hide file tree
Showing 9 changed files with 819 additions and 272 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- `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

- 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`.
With this change, the filters in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` are now working for stats handler. (#5196)

### Deprecated

- 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

Expand Down
45 changes: 35 additions & 10 deletions instrumentation/google.golang.org/grpc/otelgrpc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -20,18 +22,26 @@ const (
GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code")
)

// Filter is a predicate used to determine whether a given request in
// interceptor info should be traced. A Filter must return true if
// InterceptorFilter is a predicate used to determine whether a given request in
// interceptor info should be instrumented. A InterceptorFilter must return true if
// the request should be traced.
type Filter func(*InterceptorInfo) bool
//
// Deprecated: Use stats handlers instead.
type InterceptorFilter func(*InterceptorInfo) bool

// Filter is a predicate used to determine whether a given request in
// 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.
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
Expand Down Expand Up @@ -152,15 +162,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
}
Expand Down
49 changes: 15 additions & 34 deletions instrumentation/google.golang.org/grpc/otelgrpc/filters/filters.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// 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 instrumented.
package filters // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/filters"

import (
"path"
"strings"

"google.golang.org/grpc/stats"

"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
)

Expand All @@ -19,20 +23,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, "/")
Expand All @@ -47,7 +39,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
Expand All @@ -60,7 +52,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
Expand All @@ -78,15 +70,15 @@ 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)
}
}

// 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
}
Expand All @@ -95,7 +87,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)
}
Expand All @@ -105,26 +97,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
}
Expand All @@ -133,7 +114,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)
}
Expand Down
Loading

0 comments on commit 0483033

Please sign in to comment.