@@ -21,6 +21,7 @@ import (
2121 "log"
2222 "strings"
2323
24+ "go.opentelemetry.io/otel/attribute"
2425 otelcodes "go.opentelemetry.io/otel/codes"
2526 "go.opentelemetry.io/otel/trace"
2627 "google.golang.org/grpc"
@@ -83,7 +84,10 @@ func (h *clientTracingHandler) finishTrace(err error, ts trace.Span) {
8384// It creates a new outgoing carrier which serializes information about this
8485// span into gRPC Metadata, if TextMapPropagator is provided in the trace
8586// 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 {
8791 // Add a "Delayed name resolution complete" event to the call span
8892 // if there was name resolution delay. In case of multiple retry attempts,
8993 // ensure that event is added only once.
@@ -98,7 +102,7 @@ func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo,
98102 carrier := otelinternaltracing .NewOutgoingCarrier (ctx )
99103 h .options .TraceOptions .TextMapPropagator .Inject (ctx , carrier )
100104 ai .traceSpan = span
101- return carrier .Context (), ai
105+ return carrier .Context ()
102106}
103107
104108// createCallTraceSpan creates a call span to put in the provided context using
@@ -121,7 +125,12 @@ func (h *clientTracingHandler) HandleConn(context.Context, stats.ConnStats) {}
121125// TagRPC implements per RPC attempt context management for traces.
122126func (h * clientTracingHandler ) TagRPC (ctx context.Context , info * stats.RPCTagInfo ) context.Context {
123127 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 )
125134 return setRPCInfo (ctx , & rpcInfo {ai : ai })
126135}
127136
@@ -132,5 +141,15 @@ func (h *clientTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats)
132141 logger .Error ("ctx passed into client side tracing handler trace event handling has no client attempt data present" )
133142 return
134143 }
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+ }
135154 populateSpan (rs , ri .ai )
136155}
0 commit comments