@@ -17,13 +17,16 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.g
17
17
import (
18
18
"context"
19
19
"sync/atomic"
20
+ "time"
20
21
21
22
grpc_codes "google.golang.org/grpc/codes"
22
23
"google.golang.org/grpc/stats"
23
24
"google.golang.org/grpc/status"
24
25
25
26
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal"
27
+ "go.opentelemetry.io/otel/attribute"
26
28
"go.opentelemetry.io/otel/codes"
29
+ "go.opentelemetry.io/otel/metric"
27
30
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
28
31
"go.opentelemetry.io/otel/trace"
29
32
)
@@ -33,24 +36,32 @@ type gRPCContextKey struct{}
33
36
type gRPCContext struct {
34
37
messagesReceived int64
35
38
messagesSent int64
39
+ metricAttrs []attribute.KeyValue
40
+ }
41
+
42
+ type serverHandler struct {
43
+ * config
36
44
}
37
45
38
46
// NewServerHandler creates a stats.Handler for gRPC server.
39
47
func NewServerHandler (opts ... Option ) stats.Handler {
40
48
h := & serverHandler {
41
- config : newConfig (opts ),
49
+ config : newConfig (opts , "server" ),
42
50
}
43
51
44
- h .tracer = h .config .TracerProvider .Tracer (
45
- instrumentationName ,
46
- trace .WithInstrumentationVersion (SemVersion ()),
47
- )
48
52
return h
49
53
}
50
54
51
- type serverHandler struct {
52
- * config
53
- tracer trace.Tracer
55
+ // TagConn can attach some information to the given context.
56
+ func (h * serverHandler ) TagConn (ctx context.Context , info * stats.ConnTagInfo ) context.Context {
57
+ span := trace .SpanFromContext (ctx )
58
+ attrs := peerAttr (peerFromCtx (ctx ))
59
+ span .SetAttributes (attrs ... )
60
+ return ctx
61
+ }
62
+
63
+ // HandleConn processes the Conn stats.
64
+ func (h * serverHandler ) HandleConn (ctx context.Context , info stats.ConnStats ) {
54
65
}
55
66
56
67
// TagRPC can attach some information to the given context.
@@ -66,46 +77,30 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
66
77
trace .WithAttributes (attrs ... ),
67
78
)
68
79
69
- gctx := gRPCContext {}
80
+ gctx := gRPCContext {
81
+ metricAttrs : attrs ,
82
+ }
70
83
return context .WithValue (ctx , gRPCContextKey {}, & gctx )
71
84
}
72
85
73
86
// HandleRPC processes the RPC stats.
74
87
func (h * serverHandler ) HandleRPC (ctx context.Context , rs stats.RPCStats ) {
75
- handleRPC (ctx , rs )
76
- }
77
-
78
- // TagConn can attach some information to the given context.
79
- func (h * serverHandler ) TagConn (ctx context.Context , info * stats.ConnTagInfo ) context.Context {
80
- span := trace .SpanFromContext (ctx )
81
- attrs := peerAttr (peerFromCtx (ctx ))
82
- span .SetAttributes (attrs ... )
83
- return ctx
88
+ h .handleRPC (ctx , rs )
84
89
}
85
90
86
- // HandleConn processes the Conn stats.
87
- func ( h * serverHandler ) HandleConn ( ctx context. Context , info stats. ConnStats ) {
91
+ type clientHandler struct {
92
+ * config
88
93
}
89
94
90
95
// NewClientHandler creates a stats.Handler for gRPC client.
91
96
func NewClientHandler (opts ... Option ) stats.Handler {
92
97
h := & clientHandler {
93
- config : newConfig (opts ),
98
+ config : newConfig (opts , "client" ),
94
99
}
95
100
96
- h .tracer = h .config .TracerProvider .Tracer (
97
- instrumentationName ,
98
- trace .WithInstrumentationVersion (SemVersion ()),
99
- )
100
-
101
101
return h
102
102
}
103
103
104
- type clientHandler struct {
105
- * config
106
- tracer trace.Tracer
107
- }
108
-
109
104
// TagRPC can attach some information to the given context.
110
105
func (h * clientHandler ) TagRPC (ctx context.Context , info * stats.RPCTagInfo ) context.Context {
111
106
name , attrs := internal .ParseFullMethod (info .FullMethodName )
@@ -117,14 +112,16 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
117
112
trace .WithAttributes (attrs ... ),
118
113
)
119
114
120
- gctx := gRPCContext {}
115
+ gctx := gRPCContext {
116
+ metricAttrs : attrs ,
117
+ }
121
118
122
119
return inject (context .WithValue (ctx , gRPCContextKey {}, & gctx ), h .config .Propagators )
123
120
}
124
121
125
122
// HandleRPC processes the RPC stats.
126
123
func (h * clientHandler ) HandleRPC (ctx context.Context , rs stats.RPCStats ) {
127
- handleRPC (ctx , rs )
124
+ h . handleRPC (ctx , rs )
128
125
}
129
126
130
127
// TagConn can attach some information to the given context.
@@ -140,17 +137,22 @@ func (h *clientHandler) HandleConn(context.Context, stats.ConnStats) {
140
137
// no-op
141
138
}
142
139
143
- func handleRPC (ctx context.Context , rs stats.RPCStats ) {
140
+ func ( c * config ) handleRPC (ctx context.Context , rs stats.RPCStats ) {
144
141
span := trace .SpanFromContext (ctx )
145
142
gctx , _ := ctx .Value (gRPCContextKey {}).(* gRPCContext )
146
143
var messageId int64
144
+ metricAttrs := make ([]attribute.KeyValue , 0 , len (gctx .metricAttrs )+ 1 )
145
+ metricAttrs = append (metricAttrs , gctx .metricAttrs ... )
146
+ wctx := withoutCancel (ctx )
147
147
148
148
switch rs := rs .(type ) {
149
149
case * stats.Begin :
150
150
case * stats.InPayload :
151
151
if gctx != nil {
152
152
messageId = atomic .AddInt64 (& gctx .messagesReceived , 1 )
153
+ c .rpcRequestSize .Record (wctx , int64 (rs .Length ), metric .WithAttributes (metricAttrs ... ))
153
154
}
155
+
154
156
span .AddEvent ("message" ,
155
157
trace .WithAttributes (
156
158
semconv .MessageTypeReceived ,
@@ -162,6 +164,7 @@ func handleRPC(ctx context.Context, rs stats.RPCStats) {
162
164
case * stats.OutPayload :
163
165
if gctx != nil {
164
166
messageId = atomic .AddInt64 (& gctx .messagesSent , 1 )
167
+ c .rpcResponseSize .Record (wctx , int64 (rs .Length ), metric .WithAttributes (metricAttrs ... ))
165
168
}
166
169
167
170
span .AddEvent ("message" ,
@@ -172,16 +175,57 @@ func handleRPC(ctx context.Context, rs stats.RPCStats) {
172
175
semconv .MessageUncompressedSizeKey .Int (rs .Length ),
173
176
),
174
177
)
178
+ case * stats.OutTrailer :
175
179
case * stats.End :
180
+ var rpcStatusAttr attribute.KeyValue
181
+
176
182
if rs .Error != nil {
177
183
s , _ := status .FromError (rs .Error )
178
184
span .SetStatus (codes .Error , s .Message ())
179
- span . SetAttributes ( statusCodeAttr (s .Code ()))
185
+ rpcStatusAttr = semconv . RPCGRPCStatusCodeKey . Int ( int (s .Code ()))
180
186
} else {
181
- span . SetAttributes ( statusCodeAttr (grpc_codes .OK ))
187
+ rpcStatusAttr = semconv . RPCGRPCStatusCodeKey . Int ( int (grpc_codes .OK ))
182
188
}
189
+ span .SetAttributes (rpcStatusAttr )
183
190
span .End ()
191
+
192
+ metricAttrs = append (metricAttrs , rpcStatusAttr )
193
+ c .rpcDuration .Record (wctx , float64 (rs .EndTime .Sub (rs .BeginTime )), metric .WithAttributes (metricAttrs ... ))
194
+ c .rpcRequestsPerRPC .Record (wctx , gctx .messagesReceived , metric .WithAttributes (metricAttrs ... ))
195
+ c .rpcResponsesPerRPC .Record (wctx , gctx .messagesSent , metric .WithAttributes (metricAttrs ... ))
196
+
184
197
default :
185
198
return
186
199
}
187
200
}
201
+
202
+ func withoutCancel (parent context.Context ) context.Context {
203
+ if parent == nil {
204
+ panic ("cannot create context from nil parent" )
205
+ }
206
+ return withoutCancelCtx {parent }
207
+ }
208
+
209
+ type withoutCancelCtx struct {
210
+ c context.Context
211
+ }
212
+
213
+ func (withoutCancelCtx ) Deadline () (deadline time.Time , ok bool ) {
214
+ return
215
+ }
216
+
217
+ func (withoutCancelCtx ) Done () <- chan struct {} {
218
+ return nil
219
+ }
220
+
221
+ func (withoutCancelCtx ) Err () error {
222
+ return nil
223
+ }
224
+
225
+ func (w withoutCancelCtx ) Value (key any ) any {
226
+ return w .c .Value (key )
227
+ }
228
+
229
+ func (w withoutCancelCtx ) String () string {
230
+ return "withoutCancel"
231
+ }
0 commit comments