28
28
import java .util .Map ;
29
29
import java .util .Objects ;
30
30
import java .util .concurrent .CompletableFuture ;
31
+ import java .util .concurrent .CompletionException ;
31
32
import java .util .stream .Collectors ;
32
33
33
34
import org .apache .commons .logging .LogFactory ;
73
74
import org .springframework .util .ObjectUtils ;
74
75
import org .springframework .util .StringUtils ;
75
76
77
+ import io .micrometer .observation .Observation ;
78
+ import io .micrometer .observation .ObservationRegistry ;
76
79
import reactor .core .publisher .Mono ;
77
80
78
81
/**
@@ -153,6 +156,8 @@ public abstract class MessagingMessageListenerAdapter<K, V> implements ConsumerS
153
156
154
157
private String correlationHeaderName = KafkaHeaders .CORRELATION_ID ;
155
158
159
+ private ObservationRegistry observationRegistry = ObservationRegistry .NOOP ;
160
+
156
161
/**
157
162
* Create an instance with the provided bean and method.
158
163
* @param bean the bean.
@@ -247,6 +252,15 @@ public void setHandlerMethod(HandlerAdapter handlerMethod) {
247
252
this .handlerMethod = handlerMethod ;
248
253
}
249
254
255
+ /**
256
+ * Set the {@link ObservationRegistry} to handle observability.
257
+ * @param observationRegistry {@link ObservationRegistry} instance.
258
+ * @since 3.3.0
259
+ */
260
+ public void setObservationRegistry (ObservationRegistry observationRegistry ) {
261
+ this .observationRegistry = observationRegistry ;
262
+ }
263
+
250
264
public boolean isAsyncReplies () {
251
265
return this .handlerMethod != null && this .handlerMethod .isAsyncReplies ();
252
266
}
@@ -382,15 +396,34 @@ protected Message<?> toMessagingMessage(ConsumerRecord<K, V> cRecord, @Nullable
382
396
protected void invoke (Object records , @ Nullable Acknowledgment acknowledgment , Consumer <?, ?> consumer ,
383
397
final Message <?> message ) {
384
398
399
+ Throwable listenerError = null ;
400
+ Object result = null ;
401
+ Observation currentObservation = getCurrentObservation ();
385
402
try {
386
- Object result = invokeHandler (records , acknowledgment , message , consumer );
403
+ result = invokeHandler (records , acknowledgment , message , consumer );
387
404
if (result != null ) {
388
405
handleResult (result , records , acknowledgment , consumer , message );
389
406
}
390
407
}
391
- catch (ListenerExecutionFailedException e ) { // NOSONAR ex flow control
408
+ catch (ListenerExecutionFailedException e ) {
409
+ listenerError = e ;
410
+ currentObservation .error (e );
392
411
handleException (records , acknowledgment , consumer , message , e );
393
412
}
413
+ catch (Error e ) {
414
+ listenerError = e ;
415
+ currentObservation .error (e );
416
+ }
417
+ finally {
418
+ if (listenerError != null || result == null ) {
419
+ currentObservation .stop ();
420
+ }
421
+ }
422
+ }
423
+
424
+ private Observation getCurrentObservation () {
425
+ Observation currentObservation = this .observationRegistry .getCurrentObservation ();
426
+ return currentObservation == null ? Observation .NOOP : currentObservation ;
394
427
}
395
428
396
429
/**
@@ -402,6 +435,7 @@ protected void invoke(Object records, @Nullable Acknowledgment acknowledgment, C
402
435
* @param consumer the consumer.
403
436
* @return the result of invocation.
404
437
*/
438
+ @ Nullable
405
439
protected final Object invokeHandler (Object data , @ Nullable Acknowledgment acknowledgment , Message <?> message ,
406
440
Consumer <?, ?> consumer ) {
407
441
@@ -460,7 +494,7 @@ private RuntimeException checkAckArg(@Nullable Acknowledgment acknowledgment, Me
460
494
*/
461
495
protected void handleResult (Object resultArg , Object request , @ Nullable Acknowledgment acknowledgment ,
462
496
Consumer <?, ?> consumer , @ Nullable Message <?> source ) {
463
-
497
+ final Observation observation = getCurrentObservation ();
464
498
this .logger .debug (() -> "Listener method returned result [" + resultArg
465
499
+ "] - generating response message for it" );
466
500
String replyTopic = evaluateReplyTopic (request , source , resultArg );
@@ -474,35 +508,42 @@ protected void handleResult(Object resultArg, Object request, @Nullable Acknowle
474
508
invocationResult .messageReturnType () :
475
509
this .messageReturnType ;
476
510
477
- if (result instanceof CompletableFuture <?> completable ) {
511
+ CompletableFuture <?> completableFutureResult ;
512
+
513
+ if (monoPresent && result instanceof Mono <?> mono ) {
514
+ if (acknowledgment == null || !acknowledgment .isOutOfOrderCommit ()) {
515
+ this .logger .warn ("Container 'Acknowledgment' must be async ack for Mono<?> return type " +
516
+ "(or Kotlin suspend function); otherwise the container will ack the message immediately" );
517
+ }
518
+ completableFutureResult = mono .toFuture ();
519
+ }
520
+ else if (!(result instanceof CompletableFuture <?>)) {
521
+ completableFutureResult = CompletableFuture .completedFuture (result );
522
+ }
523
+ else {
524
+ completableFutureResult = (CompletableFuture <?>) result ;
478
525
if (acknowledgment == null || !acknowledgment .isOutOfOrderCommit ()) {
479
526
this .logger .warn ("Container 'Acknowledgment' must be async ack for Future<?> return type; "
480
527
+ "otherwise the container will ack the message immediately" );
481
528
}
482
- completable .whenComplete ((r , t ) -> {
529
+ }
530
+
531
+ completableFutureResult .whenComplete ((r , t ) -> {
532
+ try {
483
533
if (t == null ) {
484
534
asyncSuccess (r , replyTopic , source , messageReturnType );
485
535
acknowledge (acknowledgment );
486
536
}
487
537
else {
488
- asyncFailure (request , acknowledgment , consumer , t , source );
538
+ Throwable cause = t instanceof CompletionException ? t .getCause () : t ;
539
+ observation .error (cause );
540
+ asyncFailure (request , acknowledgment , consumer , cause , source );
489
541
}
490
- });
491
- }
492
- else if (monoPresent && result instanceof Mono <?> mono ) {
493
- if (acknowledgment == null || !acknowledgment .isOutOfOrderCommit ()) {
494
- this .logger .warn ("Container 'Acknowledgment' must be async ack for Mono<?> return type " +
495
- "(or Kotlin suspend function); otherwise the container will ack the message immediately" );
496
542
}
497
- mono .subscribe (
498
- r -> asyncSuccess (r , replyTopic , source , messageReturnType ),
499
- t -> asyncFailure (request , acknowledgment , consumer , t , source ),
500
- () -> acknowledge (acknowledgment )
501
- );
502
- }
503
- else {
504
- sendResponse (result , replyTopic , source , messageReturnType );
505
- }
543
+ finally {
544
+ observation .stop ();
545
+ }
546
+ });
506
547
}
507
548
508
549
@ Nullable
0 commit comments