// Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "go.opentelemetry.io/otel/trace" ) const ( // instrumentationName is the name of this instrumentation package. instrumentationName = "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" // GRPCStatusCodeKey is convention for numeric status code of a gRPC request. 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 // the request should be traced. type Filter func(*InterceptorInfo) bool // config is a group of options for this instrumentation. type config struct { Filter Filter Propagators propagation.TextMapPropagator TracerProvider trace.TracerProvider MeterProvider metric.MeterProvider meter metric.Meter rpcServerDuration metric.Int64Histogram } // Option applies an option value for a config. type Option interface { apply(*config) } // newConfig returns a config configured with all the passed Options. func newConfig(opts []Option) *config { c := &config{ Propagators: otel.GetTextMapPropagator(), TracerProvider: otel.GetTracerProvider(), MeterProvider: otel.GetMeterProvider(), } for _, o := range opts { o.apply(c) } c.meter = c.MeterProvider.Meter( instrumentationName, metric.WithInstrumentationVersion(Version()), metric.WithSchemaURL(semconv.SchemaURL), ) var err error if c.rpcServerDuration, err = c.meter.Int64Histogram("rpc.server.duration", metric.WithUnit("ms")); err != nil { otel.Handle(err) } return c } type propagatorsOption struct{ p propagation.TextMapPropagator } func (o propagatorsOption) apply(c *config) { if o.p != nil { c.Propagators = o.p } } // WithPropagators returns an Option to use the Propagators when extracting // and injecting trace context from requests. func WithPropagators(p propagation.TextMapPropagator) Option { return propagatorsOption{p: p} } type tracerProviderOption struct{ tp trace.TracerProvider } func (o tracerProviderOption) apply(c *config) { if o.tp != nil { c.TracerProvider = o.tp } } // WithInterceptorFilter returns an Option to use the request filter. func WithInterceptorFilter(f Filter) Option { return interceptorFilterOption{f: f} } type interceptorFilterOption struct { f Filter } func (o interceptorFilterOption) apply(c *config) { if o.f != nil { c.Filter = o.f } } // WithTracerProvider returns an Option to use the TracerProvider when // creating a Tracer. func WithTracerProvider(tp trace.TracerProvider) Option { return tracerProviderOption{tp: tp} } type meterProviderOption struct{ mp metric.MeterProvider } func (o meterProviderOption) apply(c *config) { if o.mp != nil { c.MeterProvider = o.mp } } // WithMeterProvider returns an Option to use the MeterProvider when // creating a Meter. If this option is not provide the global MeterProvider will be used. func WithMeterProvider(mp metric.MeterProvider) Option { return meterProviderOption{mp: mp} }