@@ -21,7 +21,10 @@ import (
21
21
"sync/atomic"
22
22
"time"
23
23
24
+ otelcodes "go.opentelemetry.io/otel/codes"
25
+ "go.opentelemetry.io/otel/trace"
24
26
"google.golang.org/grpc"
27
+ grpccodes "google.golang.org/grpc/codes"
25
28
estats "google.golang.org/grpc/experimental/stats"
26
29
istats "google.golang.org/grpc/internal/stats"
27
30
"google.golang.org/grpc/metadata"
@@ -85,8 +88,12 @@ func (h *clientStatsHandler) unaryInterceptor(ctx context.Context, method string
85
88
}
86
89
87
90
startTime := time .Now ()
91
+ var span trace.Span
92
+ if h .options .isTracingEnabled () {
93
+ ctx , span = h .createCallTraceSpan (ctx , method )
94
+ }
88
95
err := invoker (ctx , method , req , reply , cc , opts ... )
89
- h .perCallMetrics (ctx , err , startTime , ci )
96
+ h .perCallTracesAndMetrics (ctx , err , startTime , ci , span )
90
97
return err
91
98
}
92
99
@@ -119,22 +126,37 @@ func (h *clientStatsHandler) streamInterceptor(ctx context.Context, desc *grpc.S
119
126
}
120
127
121
128
startTime := time .Now ()
122
-
129
+ var span trace.Span
130
+ if h .options .isTracingEnabled () {
131
+ ctx , span = h .createCallTraceSpan (ctx , method )
132
+ }
123
133
callback := func (err error ) {
124
- h .perCallMetrics (ctx , err , startTime , ci )
134
+ h .perCallTracesAndMetrics (ctx , err , startTime , ci , span )
125
135
}
126
136
opts = append ([]grpc.CallOption {grpc .OnFinish (callback )}, opts ... )
127
137
return streamer (ctx , desc , cc , method , opts ... )
128
138
}
129
139
130
- func (h * clientStatsHandler ) perCallMetrics (ctx context.Context , err error , startTime time.Time , ci * callInfo ) {
131
- callLatency := float64 (time .Since (startTime )) / float64 (time .Second ) // calculate ASAP
132
- attrs := otelmetric .WithAttributeSet (otelattribute .NewSet (
133
- otelattribute .String ("grpc.method" , ci .method ),
134
- otelattribute .String ("grpc.target" , ci .target ),
135
- otelattribute .String ("grpc.status" , canonicalString (status .Code (err ))),
136
- ))
137
- h .clientMetrics .callDuration .Record (ctx , callLatency , attrs )
140
+ // perCallTracesAndMetrics records per call trace spans and metrics.
141
+ func (h * clientStatsHandler ) perCallTracesAndMetrics (ctx context.Context , err error , startTime time.Time , ci * callInfo , ts trace.Span ) {
142
+ if h .options .isTracingEnabled () {
143
+ s := status .Convert (err )
144
+ if s .Code () == grpccodes .OK {
145
+ ts .SetStatus (otelcodes .Ok , s .Message ())
146
+ } else {
147
+ ts .SetStatus (otelcodes .Error , s .Message ())
148
+ }
149
+ ts .End ()
150
+ }
151
+ if h .options .isMetricsEnabled () {
152
+ callLatency := float64 (time .Since (startTime )) / float64 (time .Second )
153
+ attrs := otelmetric .WithAttributeSet (otelattribute .NewSet (
154
+ otelattribute .String ("grpc.method" , ci .method ),
155
+ otelattribute .String ("grpc.target" , ci .target ),
156
+ otelattribute .String ("grpc.status" , canonicalString (status .Code (err ))),
157
+ ))
158
+ h .clientMetrics .callDuration .Record (ctx , callLatency , attrs )
159
+ }
138
160
}
139
161
140
162
// TagConn exists to satisfy stats.Handler.
@@ -163,15 +185,17 @@ func (h *clientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
163
185
}
164
186
ctx = istats .SetLabels (ctx , labels )
165
187
}
166
- ai := & attemptInfo { // populates information about RPC start.
188
+ ai := & attemptInfo {
167
189
startTime : time .Now (),
168
190
xdsLabels : labels .TelemetryLabels ,
169
- method : info .FullMethodName ,
191
+ method : removeLeadingSlash ( info .FullMethodName ) ,
170
192
}
171
- ri := & rpcInfo {
172
- ai : ai ,
193
+ if h . options . isTracingEnabled () {
194
+ ctx , ai = h . traceTagRPC ( ctx , ai )
173
195
}
174
- return setRPCInfo (ctx , ri )
196
+ return setRPCInfo (ctx , & rpcInfo {
197
+ ai : ai ,
198
+ })
175
199
}
176
200
177
201
func (h * clientStatsHandler ) HandleRPC (ctx context.Context , rs stats.RPCStats ) {
@@ -180,7 +204,12 @@ func (h *clientStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
180
204
logger .Error ("ctx passed into client side stats handler metrics event handling has no client attempt data present" )
181
205
return
182
206
}
183
- h .processRPCEvent (ctx , rs , ri .ai )
207
+ if h .options .isMetricsEnabled () {
208
+ h .processRPCEvent (ctx , rs , ri .ai )
209
+ }
210
+ if h .options .isTracingEnabled () {
211
+ populateSpan (rs , ri .ai )
212
+ }
184
213
}
185
214
186
215
func (h * clientStatsHandler ) processRPCEvent (ctx context.Context , s stats.RPCStats , ai * attemptInfo ) {
0 commit comments