...
1
2
3
4 package otelgrpc
5
6 import (
7 "go.opentelemetry.io/otel"
8 "go.opentelemetry.io/otel/attribute"
9 "go.opentelemetry.io/otel/metric"
10 "go.opentelemetry.io/otel/metric/noop"
11 "go.opentelemetry.io/otel/propagation"
12 semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
13 "go.opentelemetry.io/otel/trace"
14 )
15
16 const (
17
18 ScopeName = "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
19
20 GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code")
21 )
22
23
24
25
26 type Filter func(*InterceptorInfo) bool
27
28
29 type config struct {
30 Filter Filter
31 Propagators propagation.TextMapPropagator
32 TracerProvider trace.TracerProvider
33 MeterProvider metric.MeterProvider
34 SpanStartOptions []trace.SpanStartOption
35
36 ReceivedEvent bool
37 SentEvent bool
38
39 tracer trace.Tracer
40 meter metric.Meter
41
42 rpcDuration metric.Float64Histogram
43 rpcRequestSize metric.Int64Histogram
44 rpcResponseSize metric.Int64Histogram
45 rpcRequestsPerRPC metric.Int64Histogram
46 rpcResponsesPerRPC metric.Int64Histogram
47 }
48
49
50 type Option interface {
51 apply(*config)
52 }
53
54
55 func newConfig(opts []Option, role string) *config {
56 c := &config{
57 Propagators: otel.GetTextMapPropagator(),
58 TracerProvider: otel.GetTracerProvider(),
59 MeterProvider: otel.GetMeterProvider(),
60 }
61 for _, o := range opts {
62 o.apply(c)
63 }
64
65 c.tracer = c.TracerProvider.Tracer(
66 ScopeName,
67 trace.WithInstrumentationVersion(SemVersion()),
68 )
69
70 c.meter = c.MeterProvider.Meter(
71 ScopeName,
72 metric.WithInstrumentationVersion(Version()),
73 metric.WithSchemaURL(semconv.SchemaURL),
74 )
75
76 var err error
77 c.rpcDuration, err = c.meter.Float64Histogram("rpc."+role+".duration",
78 metric.WithDescription("Measures the duration of inbound RPC."),
79 metric.WithUnit("ms"))
80 if err != nil {
81 otel.Handle(err)
82 if c.rpcDuration == nil {
83 c.rpcDuration = noop.Float64Histogram{}
84 }
85 }
86
87 c.rpcRequestSize, err = c.meter.Int64Histogram("rpc."+role+".request.size",
88 metric.WithDescription("Measures size of RPC request messages (uncompressed)."),
89 metric.WithUnit("By"))
90 if err != nil {
91 otel.Handle(err)
92 if c.rpcRequestSize == nil {
93 c.rpcRequestSize = noop.Int64Histogram{}
94 }
95 }
96
97 c.rpcResponseSize, err = c.meter.Int64Histogram("rpc."+role+".response.size",
98 metric.WithDescription("Measures size of RPC response messages (uncompressed)."),
99 metric.WithUnit("By"))
100 if err != nil {
101 otel.Handle(err)
102 if c.rpcResponseSize == nil {
103 c.rpcResponseSize = noop.Int64Histogram{}
104 }
105 }
106
107 c.rpcRequestsPerRPC, err = c.meter.Int64Histogram("rpc."+role+".requests_per_rpc",
108 metric.WithDescription("Measures the number of messages received per RPC. Should be 1 for all non-streaming RPCs."),
109 metric.WithUnit("{count}"))
110 if err != nil {
111 otel.Handle(err)
112 if c.rpcRequestsPerRPC == nil {
113 c.rpcRequestsPerRPC = noop.Int64Histogram{}
114 }
115 }
116
117 c.rpcResponsesPerRPC, err = c.meter.Int64Histogram("rpc."+role+".responses_per_rpc",
118 metric.WithDescription("Measures the number of messages received per RPC. Should be 1 for all non-streaming RPCs."),
119 metric.WithUnit("{count}"))
120 if err != nil {
121 otel.Handle(err)
122 if c.rpcResponsesPerRPC == nil {
123 c.rpcResponsesPerRPC = noop.Int64Histogram{}
124 }
125 }
126
127 return c
128 }
129
130 type propagatorsOption struct{ p propagation.TextMapPropagator }
131
132 func (o propagatorsOption) apply(c *config) {
133 if o.p != nil {
134 c.Propagators = o.p
135 }
136 }
137
138
139
140 func WithPropagators(p propagation.TextMapPropagator) Option {
141 return propagatorsOption{p: p}
142 }
143
144 type tracerProviderOption struct{ tp trace.TracerProvider }
145
146 func (o tracerProviderOption) apply(c *config) {
147 if o.tp != nil {
148 c.TracerProvider = o.tp
149 }
150 }
151
152
153
154
155 func WithInterceptorFilter(f Filter) Option {
156 return interceptorFilterOption{f: f}
157 }
158
159 type interceptorFilterOption struct {
160 f Filter
161 }
162
163 func (o interceptorFilterOption) apply(c *config) {
164 if o.f != nil {
165 c.Filter = o.f
166 }
167 }
168
169
170
171 func WithTracerProvider(tp trace.TracerProvider) Option {
172 return tracerProviderOption{tp: tp}
173 }
174
175 type meterProviderOption struct{ mp metric.MeterProvider }
176
177 func (o meterProviderOption) apply(c *config) {
178 if o.mp != nil {
179 c.MeterProvider = o.mp
180 }
181 }
182
183
184
185 func WithMeterProvider(mp metric.MeterProvider) Option {
186 return meterProviderOption{mp: mp}
187 }
188
189
190 type Event int
191
192
193 const (
194 ReceivedEvents Event = iota
195 SentEvents
196 )
197
198 type messageEventsProviderOption struct {
199 events []Event
200 }
201
202 func (m messageEventsProviderOption) apply(c *config) {
203 for _, e := range m.events {
204 switch e {
205 case ReceivedEvents:
206 c.ReceivedEvent = true
207 case SentEvents:
208 c.SentEvent = true
209 }
210 }
211 }
212
213
214
215
216
217
218
219
220 func WithMessageEvents(events ...Event) Option {
221 return messageEventsProviderOption{events: events}
222 }
223
224 type spanStartOption struct{ opts []trace.SpanStartOption }
225
226 func (o spanStartOption) apply(c *config) {
227 c.SpanStartOptions = append(c.SpanStartOptions, o.opts...)
228 }
229
230
231
232 func WithSpanOptions(opts ...trace.SpanStartOption) Option {
233 return spanStartOption{opts}
234 }
235
View as plain text