@@ -21,6 +21,7 @@ import (
21
21
"log"
22
22
"strings"
23
23
24
+ "go.opentelemetry.io/otel/attribute"
24
25
otelcodes "go.opentelemetry.io/otel/codes"
25
26
"go.opentelemetry.io/otel/trace"
26
27
"google.golang.org/grpc"
@@ -83,7 +84,10 @@ func (h *clientTracingHandler) finishTrace(err error, ts trace.Span) {
83
84
// It creates a new outgoing carrier which serializes information about this
84
85
// span into gRPC Metadata, if TextMapPropagator is provided in the trace
85
86
// options. if TextMapPropagator is not provided, it returns the context as is.
86
- func (h * clientTracingHandler ) traceTagRPC (ctx context.Context , ai * attemptInfo , nameResolutionDelayed bool ) (context.Context , * attemptInfo ) {
87
+ //
88
+ // Note: The passed attemptInfo pointer (ai) is mutated in-place. Fields such as
89
+ // ai.traceSpan are updated directly. No new attemptInfo is returned.
90
+ func (h * clientTracingHandler ) traceTagRPC (ctx context.Context , ai * attemptInfo , nameResolutionDelayed bool ) context.Context {
87
91
// Add a "Delayed name resolution complete" event to the call span
88
92
// if there was name resolution delay. In case of multiple retry attempts,
89
93
// ensure that event is added only once.
@@ -98,7 +102,7 @@ func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo,
98
102
carrier := otelinternaltracing .NewOutgoingCarrier (ctx )
99
103
h .options .TraceOptions .TextMapPropagator .Inject (ctx , carrier )
100
104
ai .traceSpan = span
101
- return carrier .Context (), ai
105
+ return carrier .Context ()
102
106
}
103
107
104
108
// createCallTraceSpan creates a call span to put in the provided context using
@@ -121,7 +125,12 @@ func (h *clientTracingHandler) HandleConn(context.Context, stats.ConnStats) {}
121
125
// TagRPC implements per RPC attempt context management for traces.
122
126
func (h * clientTracingHandler ) TagRPC (ctx context.Context , info * stats.RPCTagInfo ) context.Context {
123
127
ctx , ai := getOrCreateRPCAttemptInfo (ctx )
124
- ctx , ai = h .traceTagRPC (ctx , ai , info .NameResolutionDelay )
128
+ ci := getCallInfo (ctx )
129
+ if ci == nil {
130
+ logger .Error ("context passed into client side stats handler (TagRPC) has no call info" )
131
+ return ctx
132
+ }
133
+ ctx = h .traceTagRPC (ctx , ai , info .NameResolutionDelay )
125
134
return setRPCInfo (ctx , & rpcInfo {ai : ai })
126
135
}
127
136
@@ -132,5 +141,15 @@ func (h *clientTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats)
132
141
logger .Error ("ctx passed into client side tracing handler trace event handling has no client attempt data present" )
133
142
return
134
143
}
144
+
145
+ // Client-specific Begin attributes.
146
+ if begin , ok := rs .(* stats.Begin ); ok {
147
+ ci := getCallInfo (ctx )
148
+ previousRPCAttempts := ci .previousRPCAttempts .Add (1 ) - 1
149
+ ri .ai .traceSpan .SetAttributes (
150
+ attribute .Int64 ("previous-rpc-attempts" , int64 (previousRPCAttempts )),
151
+ attribute .Bool ("transparent-retry" , begin .IsTransparentRetryAttempt ),
152
+ )
153
+ }
135
154
populateSpan (rs , ri .ai )
136
155
}
0 commit comments