20
20
import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .BACKEND_SERVICE_KEY ;
21
21
import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .LOCALITY_KEY ;
22
22
import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .METHOD_KEY ;
23
+ import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .RETRY_TYPE_KEY ;
23
24
import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .STATUS_KEY ;
24
25
import static io .grpc .opentelemetry .internal .OpenTelemetryConstants .TARGET_KEY ;
25
26
44
45
import io .grpc .Status ;
45
46
import io .grpc .Status .Code ;
46
47
import io .grpc .StreamTracer ;
48
+ import io .grpc .opentelemetry .internal .OpenTelemetryConstants ;
47
49
import io .opentelemetry .api .common .AttributesBuilder ;
48
50
import java .util .ArrayList ;
49
51
import java .util .Collection ;
71
73
*/
72
74
final class OpenTelemetryMetricsModule {
73
75
private static final Logger logger = Logger .getLogger (OpenTelemetryMetricsModule .class .getName ());
76
+ private static final double NANOS_PER_SEC = 1_000_000_000.0 ;
74
77
public static final ImmutableSet <String > DEFAULT_PER_CALL_METRICS_SET =
75
78
ImmutableSet .of (
76
79
"grpc.client.attempt.started" ,
@@ -292,9 +295,11 @@ static final class CallAttemptsTracerFactory extends ClientStreamTracer.Factory
292
295
private final String fullMethodName ;
293
296
private final List <OpenTelemetryPlugin .ClientCallPlugin > callPlugins ;
294
297
private Status status ;
298
+ private long retryDelayNanos ;
295
299
private long callLatencyNanos ;
296
300
private final Object lock = new Object ();
297
301
private final AtomicLong attemptsPerCall = new AtomicLong ();
302
+ private final AtomicLong transparentRetriesPerCall = new AtomicLong ();
298
303
@ GuardedBy ("lock" )
299
304
private int activeStreams ;
300
305
@ GuardedBy ("lock" )
@@ -331,6 +336,7 @@ public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata metada
331
336
}
332
337
if (++activeStreams == 1 && attemptStopwatch .isRunning ()) {
333
338
attemptStopwatch .stop ();
339
+ retryDelayNanos = attemptStopwatch .elapsed (TimeUnit .NANOSECONDS );
334
340
}
335
341
}
336
342
// Skip recording for the first time, since it is already recorded in
@@ -344,7 +350,9 @@ public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata metada
344
350
module .resource .clientAttemptCountCounter ().add (1 , attribute );
345
351
}
346
352
}
347
- if (!info .isTransparentRetry ()) {
353
+ if (info .isTransparentRetry ()) {
354
+ transparentRetriesPerCall .incrementAndGet ();
355
+ } else {
348
356
attemptsPerCall .incrementAndGet ();
349
357
}
350
358
return newClientTracer (info );
@@ -407,14 +415,49 @@ void recordFinishedCall() {
407
415
tracer .recordFinishedAttempt ();
408
416
}
409
417
callLatencyNanos = callStopWatch .elapsed (TimeUnit .NANOSECONDS );
410
- io .opentelemetry .api .common .Attributes attribute =
411
- io .opentelemetry .api .common .Attributes .of (METHOD_KEY , fullMethodName ,
412
- TARGET_KEY , target ,
413
- STATUS_KEY , status .getCode ().toString ());
414
418
415
419
if (module .resource .clientCallDurationCounter () != null ) {
416
- module .resource .clientCallDurationCounter ()
417
- .record (callLatencyNanos * SECONDS_PER_NANO , attribute );
420
+ long retriesPerCall = 0 ;
421
+ long attempts = attemptsPerCall .get ();
422
+ if (attempts > 0 ) {
423
+ retriesPerCall = attempts - 1 ;
424
+ }
425
+
426
+ // Base attributes
427
+ io .opentelemetry .api .common .Attributes baseAttributes =
428
+ io .opentelemetry .api .common .Attributes .of (
429
+ METHOD_KEY , fullMethodName ,
430
+ TARGET_KEY , target
431
+ );
432
+
433
+ // Duration
434
+ module .resource .clientCallDurationCounter ().record (
435
+ callLatencyNanos * SECONDS_PER_NANO ,
436
+ baseAttributes .toBuilder ()
437
+ .put (STATUS_KEY , status .getCode ().toString ())
438
+ .build ()
439
+ );
440
+
441
+ // Retry counts
442
+ module .resource .clientCallRetriesCounter ().record (
443
+ retriesPerCall ,
444
+ baseAttributes .toBuilder ()
445
+ .put (RETRY_TYPE_KEY , OpenTelemetryConstants .RetryType .RETRY .getValue ())
446
+ .build ()
447
+ );
448
+
449
+ module .resource .clientCallRetriesCounter ().record (
450
+ transparentRetriesPerCall .get (),
451
+ baseAttributes .toBuilder ()
452
+ .put (RETRY_TYPE_KEY , OpenTelemetryConstants .RetryType .TRANSPARENT .getValue ())
453
+ .build ()
454
+ );
455
+
456
+ // Retry delay
457
+ module .resource .clientCallRetryDelayCounter ().record (
458
+ retryDelayNanos / NANOS_PER_SEC ,
459
+ baseAttributes
460
+ );
418
461
}
419
462
}
420
463
}
0 commit comments