@@ -203,6 +203,8 @@ private static final class ClientTracer extends ClientStreamTracer {
203203 volatile String backendService ;
204204 long attemptNanos ;
205205 Code statusCode ;
206+ @ Nullable private volatile Stopwatch activeDelayStopwatch ;
207+ @ Nullable private volatile String activeDelayType ;
206208
207209 ClientTracer (CallAttemptsTracerFactory attemptsState , OpenTelemetryMetricsModule module ,
208210 StreamInfo info , String target , String fullMethodName ,
@@ -216,6 +218,59 @@ private static final class ClientTracer extends ClientStreamTracer {
216218 this .stopwatch = module .stopwatchSupplier .get ().start ();
217219 }
218220
221+ @ Override
222+ public void streamCreated (io .grpc .Attributes transportAtts , Metadata headers ) {
223+ delayEnded ();
224+ }
225+
226+ @ Override
227+ public void delayTypeStarted (String delayType ) {
228+ delayEnded ();
229+ activeDelayType = delayType ;
230+ activeDelayStopwatch = module .stopwatchSupplier .get ().start ();
231+ }
232+
233+ @ Override
234+ public void delayEnded () {
235+ Stopwatch delayStopwatch = activeDelayStopwatch ;
236+ String delayType = activeDelayType ;
237+ if (delayStopwatch != null && delayType != null ) {
238+ delayStopwatch .stop ();
239+ long delayNanos = delayStopwatch .elapsed (TimeUnit .NANOSECONDS );
240+ activeDelayStopwatch = null ;
241+ activeDelayType = null ;
242+ if (module .resource .clientAttemptDelayCounter () != null ) {
243+ AttributesBuilder builder = io .opentelemetry .api .common .Attributes .builder ()
244+ .put (METHOD_KEY , fullMethodName )
245+ .put (TARGET_KEY , target )
246+ .put ("grpc.delay_type" , delayType );
247+ if (module .localityEnabled ) {
248+ String savedLocality = locality ;
249+ if (savedLocality == null ) {
250+ savedLocality = "" ;
251+ }
252+ builder .put (LOCALITY_KEY , savedLocality );
253+ }
254+ if (module .backendServiceEnabled ) {
255+ String savedBackendService = backendService ;
256+ if (savedBackendService == null ) {
257+ savedBackendService = "" ;
258+ }
259+ builder .put (BACKEND_SERVICE_KEY , savedBackendService );
260+ }
261+ if (module .customLabelEnabled ) {
262+ builder .put (
263+ CUSTOM_LABEL_KEY , info .getCallOptions ().getOption (Grpc .CALL_OPTION_CUSTOM_LABEL ));
264+ }
265+ for (OpenTelemetryPlugin .ClientStreamPlugin plugin : streamPlugins ) {
266+ plugin .addLabels (builder );
267+ }
268+ module .resource .clientAttemptDelayCounter ()
269+ .record (delayNanos * SECONDS_PER_NANO , builder .build (), attemptsState .otelContext );
270+ }
271+ }
272+ }
273+
219274 @ Override
220275 public void inboundHeaders (Metadata headers ) {
221276 for (OpenTelemetryPlugin .ClientStreamPlugin plugin : streamPlugins ) {
@@ -262,6 +317,7 @@ public void inboundTrailers(Metadata trailers) {
262317
263318 @ Override
264319 public void streamClosed (Status status ) {
320+ delayEnded ();
265321 stopwatch .stop ();
266322 attemptNanos = stopwatch .elapsed (TimeUnit .NANOSECONDS );
267323 Deadline deadline = info .getCallOptions ().getDeadline ();
0 commit comments