Skip to content

Commit c122250

Browse files
stats/opentelemetry: record retry attempts from clientStream (#8342)
Fixes: #8299 RELEASE NOTES: - stats/opentelemetry: Retry attempts (`grpc.previous-rpc-attempts`) are now recorded as span attributes for non-transparent client retries.
1 parent e60a04b commit c122250

File tree

4 files changed

+335
-278
lines changed

4 files changed

+335
-278
lines changed

stats/opentelemetry/client_tracing.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
122126
func (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

Comments
 (0)