Skip to content

Commit db1286d

Browse files
author
adibrastegarnia
committed
Add open telemetry interceptors
1 parent ac8062f commit db1286d

File tree

7 files changed

+424
-0
lines changed

7 files changed

+424
-0
lines changed

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ require (
1111
github.com/spf13/cobra v1.4.0
1212
github.com/spf13/viper v1.11.0
1313
github.com/stretchr/testify v1.7.1
14+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.32.0
15+
go.opentelemetry.io/otel v1.7.0
16+
go.opentelemetry.io/otel/trace v1.7.0
1417
go.uber.org/zap v1.21.0
1518
google.golang.org/grpc v1.46.0
1619
google.golang.org/protobuf v1.28.0
@@ -20,6 +23,8 @@ require (
2023
require (
2124
github.com/davecgh/go-spew v1.1.1 // indirect
2225
github.com/fsnotify/fsnotify v1.5.1 // indirect
26+
github.com/go-logr/logr v1.2.3 // indirect
27+
github.com/go-logr/stdr v1.2.2 // indirect
2328
github.com/hashicorp/hcl v1.0.0 // indirect
2429
github.com/inconshreveable/mousetrap v1.0.0 // indirect
2530
github.com/magiconair/properties v1.8.6 // indirect

go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
1717
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
1818
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
1919
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
20+
cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
2021
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
2122
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
2223
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
2324
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
2425
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
2526
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
27+
cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM=
2628
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
2729
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
2830
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -75,6 +77,11 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
7577
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
7678
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
7779
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
80+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
81+
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
82+
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
83+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
84+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
7885
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
7986
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
8087
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -119,6 +126,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
119126
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
120127
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
121128
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
129+
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
122130
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
123131
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
124132
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -212,6 +220,12 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
212220
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
213221
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
214222
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
223+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.32.0 h1:WenoaOMNP71oq3KkMZ/jnxI9xU/JSCLw8yZILSI2lfU=
224+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.32.0/go.mod h1:J0dBVrt7dPS/lKJyQoW0xzQiUr4r2Ik1VwPjAUWnofI=
225+
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
226+
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
227+
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
228+
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
215229
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
216230
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
217231
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -305,6 +319,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
305319
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
306320
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
307321
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
322+
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
308323
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
309324
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
310325
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -448,6 +463,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
448463
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
449464
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
450465
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
466+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
451467
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
452468
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
453469
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=

pkg/grpc/otelemetry/event.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-FileCopyrightText: 2022-present Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package otelemetry
6+
7+
import (
8+
"context"
9+
"github.com/gogo/protobuf/proto"
10+
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
11+
"go.opentelemetry.io/otel/attribute"
12+
"go.opentelemetry.io/otel/trace"
13+
)
14+
15+
// Event telemetry event
16+
type event struct {
17+
id int
18+
message interface{}
19+
messageType attribute.KeyValue
20+
}
21+
22+
// Send sends a telemetry event
23+
func (e event) Send(ctx context.Context) {
24+
span := trace.SpanFromContext(ctx)
25+
if p, ok := e.message.(proto.Message); ok {
26+
span.AddEvent("event", trace.WithAttributes(
27+
e.messageType,
28+
otelgrpc.RPCMessageIDKey.Int(e.id),
29+
otelgrpc.RPCMessageUncompressedSizeKey.Int(proto.Size(p)),
30+
))
31+
} else {
32+
span.AddEvent("event", trace.WithAttributes(
33+
e.messageType,
34+
otelgrpc.RPCMessageIDKey.Int(e.id),
35+
))
36+
}
37+
38+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-FileCopyrightText: 2022-present Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package otelemetry
6+
7+
import (
8+
"context"
9+
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
10+
"go.opentelemetry.io/otel/baggage"
11+
"go.opentelemetry.io/otel/codes"
12+
"go.opentelemetry.io/otel/trace"
13+
"google.golang.org/grpc"
14+
grpccodes "google.golang.org/grpc/codes"
15+
"google.golang.org/grpc/metadata"
16+
"google.golang.org/grpc/status"
17+
)
18+
19+
// UnaryServerTelemetryInterceptor returns a grpc.UnaryServerInterceptor suitable
20+
// for use in a grpc.NewServer call.
21+
func UnaryServerTelemetryInterceptor(opts ...InstrumentationOption) grpc.UnaryServerInterceptor {
22+
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
23+
) (interface{}, error) {
24+
requestMetadata, _ := metadata.FromIncomingContext(ctx)
25+
metadataCopy := requestMetadata.Copy()
26+
27+
instrumentation := NewInstrumentation(opts)
28+
bags, spanCtx := instrumentation.Extract(ctx, &metadataCopy)
29+
ctx = baggage.ContextWithBaggage(ctx, bags)
30+
31+
tracer := instrumentation.NewTracer(trace.WithInstrumentationVersion("1.0.0"))
32+
33+
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
34+
ctx, span := tracer.Start(
35+
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
36+
name,
37+
trace.WithSpanKind(trace.SpanKindServer),
38+
trace.WithAttributes(attr...),
39+
)
40+
41+
defer span.End()
42+
e := event{
43+
messageType: otelgrpc.RPCMessageTypeReceived,
44+
message: req,
45+
id: 1,
46+
}
47+
e.Send(ctx)
48+
49+
resp, err := handler(ctx, req)
50+
if err != nil {
51+
s, _ := status.FromError(err)
52+
span.SetStatus(codes.Error, s.Message())
53+
span.SetAttributes(statusCodeAttr(s.Code()))
54+
e = event{
55+
messageType: otelgrpc.RPCMessageTypeSent,
56+
message: s.Proto(),
57+
id: 1,
58+
}
59+
e.Send(ctx)
60+
61+
} else {
62+
span.SetAttributes(statusCodeAttr(grpccodes.OK))
63+
e = event{
64+
messageType: otelgrpc.RPCMessageTypeSent,
65+
message: resp,
66+
id: 1,
67+
}
68+
}
69+
70+
return resp, err
71+
}
72+
}
73+
74+
// StreamTelemetryServerInterceptor returns a grpc.StreamServerInterceptor suitable
75+
// for use in a grpc.NewServer call.
76+
func StreamTelemetryServerInterceptor(opts ...InstrumentationOption) grpc.StreamServerInterceptor {
77+
return func(
78+
srv interface{},
79+
stream grpc.ServerStream,
80+
info *grpc.StreamServerInfo,
81+
handler grpc.StreamHandler,
82+
) error {
83+
ctx := stream.Context()
84+
85+
requestMetadata, _ := metadata.FromIncomingContext(ctx)
86+
metadataCopy := requestMetadata.Copy()
87+
88+
instrumentation := NewInstrumentation(opts)
89+
bags, spanCtx := instrumentation.Extract(ctx, &metadataCopy)
90+
ctx = baggage.ContextWithBaggage(ctx, bags)
91+
92+
tracer := instrumentation.NewTracer(trace.WithInstrumentationVersion("1.0.0"))
93+
94+
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
95+
ctx, span := tracer.Start(
96+
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
97+
name,
98+
trace.WithSpanKind(trace.SpanKindServer),
99+
trace.WithAttributes(attr...),
100+
)
101+
102+
defer span.End()
103+
err := handler(srv, wrapServerStream(ctx, stream))
104+
105+
if err != nil {
106+
s, _ := status.FromError(err)
107+
span.SetStatus(codes.Error, s.Message())
108+
span.SetAttributes(statusCodeAttr(s.Code()))
109+
} else {
110+
span.SetAttributes(statusCodeAttr(grpccodes.OK))
111+
}
112+
113+
return err
114+
}
115+
}

pkg/grpc/otelemetry/options.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// SPDX-FileCopyrightText: 2022-present Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package otelemetry
6+
7+
import (
8+
"context"
9+
"go.opentelemetry.io/otel"
10+
"go.opentelemetry.io/otel/baggage"
11+
"go.opentelemetry.io/otel/propagation"
12+
"go.opentelemetry.io/otel/trace"
13+
"google.golang.org/grpc/metadata"
14+
)
15+
16+
// Instrumentation interface
17+
type Instrumentation interface {
18+
Extract(ctx context.Context, metadata *metadata.MD) (baggage.Baggage, trace.SpanContext)
19+
Inject(ctx context.Context, metadata *metadata.MD)
20+
}
21+
22+
// InstrumentationOptions is a group of options for this instrumentation.
23+
type InstrumentationOptions struct {
24+
propagators propagation.TextMapPropagator
25+
tracerProvider trace.TracerProvider
26+
name string
27+
}
28+
29+
func (o *InstrumentationOptions) Inject(ctx context.Context, metadata *metadata.MD) {
30+
o.propagators.Inject(ctx, &metadataInfo{
31+
metadata: metadata,
32+
})
33+
}
34+
35+
func (o *InstrumentationOptions) Extract(ctx context.Context, metadata *metadata.MD) (baggage.Baggage, trace.SpanContext) {
36+
ctx = o.propagators.Extract(ctx, &metadataInfo{
37+
metadata: metadata,
38+
})
39+
40+
return baggage.FromContext(ctx), trace.SpanContextFromContext(ctx)
41+
}
42+
43+
// InstrumentationOption function
44+
type InstrumentationOption func(*InstrumentationOptions)
45+
46+
// NewInstrumentation creates a configuration for an instrumentation using a set of given options
47+
func NewInstrumentation(opts []InstrumentationOption) *InstrumentationOptions {
48+
c := &InstrumentationOptions{
49+
propagators: otel.GetTextMapPropagator(),
50+
tracerProvider: trace.NewNoopTracerProvider(),
51+
name: "",
52+
}
53+
for _, option := range opts {
54+
option(c)
55+
}
56+
57+
return c
58+
}
59+
60+
func (o InstrumentationOptions) apply(opts ...InstrumentationOption) {
61+
for _, opt := range opts {
62+
opt(&o)
63+
}
64+
}
65+
66+
func (o InstrumentationOptions) NewTracer(opts ...trace.TracerOption) trace.Tracer {
67+
return o.tracerProvider.Tracer(o.name, opts...)
68+
}
69+
70+
// WithPropagators sets propagators
71+
func WithPropagators(propagators propagation.TextMapPropagator) InstrumentationOption {
72+
return func(options *InstrumentationOptions) {
73+
options.propagators = propagators
74+
}
75+
}
76+
77+
// WithTraceProvider sets trace provider
78+
func WithTraceProvider(tracerProvider trace.TracerProvider) InstrumentationOption {
79+
return func(options *InstrumentationOptions) {
80+
options.tracerProvider = tracerProvider
81+
}
82+
}
83+
84+
// WithInstrumentationName sets instrumentation name
85+
func WithInstrumentationName(name string) InstrumentationOption {
86+
return func(options *InstrumentationOptions) {
87+
options.name = name
88+
}
89+
}
90+
91+
var _ Instrumentation = &InstrumentationOptions{}
92+
93+
type metadataInfo struct {
94+
metadata *metadata.MD
95+
}
96+
97+
// assert that metadataSupplier implements the TextMapCarrier interface
98+
var _ propagation.TextMapCarrier = &metadataInfo{}
99+
100+
func (s *metadataInfo) Get(key string) string {
101+
values := s.metadata.Get(key)
102+
if len(values) == 0 {
103+
return ""
104+
}
105+
return values[0]
106+
}
107+
108+
func (s *metadataInfo) Set(key string, value string) {
109+
s.metadata.Set(key, value)
110+
}
111+
112+
func (s *metadataInfo) Keys() []string {
113+
out := make([]string, 0, len(*s.metadata))
114+
for key := range *s.metadata {
115+
out = append(out, key)
116+
}
117+
return out
118+
}

0 commit comments

Comments
 (0)