diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/GrpcServerObservationContext.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/GrpcServerObservationContext.java index c316416a2e..1643ff0bd5 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/GrpcServerObservationContext.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/GrpcServerObservationContext.java @@ -50,6 +50,8 @@ public class GrpcServerObservationContext extends RequestReplyReceiverContext getter) { super(getter); } @@ -140,4 +142,21 @@ public void setTrailers(Metadata trailers) { this.trailers = trailers; } + /** + * Indicate whether the request is cancelled or not. + * @return {@code true} if the request is cancelled + * @since 1.14 + */ + public boolean isCancelled() { + return this.cancelled; + } + + /** + * Set {@code true} when the request is cancelled. + * @since 1.14 + */ + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/ObservationGrpcServerCall.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/ObservationGrpcServerCall.java index bd11098470..254cdd8974 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/ObservationGrpcServerCall.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/grpc/ObservationGrpcServerCall.java @@ -67,6 +67,7 @@ public void close(Status status, Metadata trailers) { GrpcServerObservationContext context = (GrpcServerObservationContext) this.observation.getContext(); context.setStatusCode(status.getCode()); context.setTrailers(trailersToKeep); + context.setCancelled(isCancelled()); super.close(status, trailers); } diff --git a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java index 43e7dcfeb1..ae7180c260 100644 --- a/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java +++ b/micrometer-core/src/test/java/io/micrometer/core/instrument/binder/grpc/GrpcObservationTest.java @@ -536,6 +536,10 @@ void observationShouldBeCapturedByInterceptor() { SimpleRequest request = SimpleRequest.newBuilder().setRequestMessage("Hello").build(); stub.unaryRpc(request); + // await until server side processing finishes, otherwise context name might + // not be populated. + await().until(serverHandler::isContextStopped); + assertThat(scopeAwareServerInterceptor.lastObservation).isNotNull().satisfies((observation -> { assertThat(observation.getContext().getContextualName()) .isEqualTo("grpc.testing.SimpleService/UnaryRpc"); @@ -570,7 +574,11 @@ void cancel() { assertThat(future.isCancelled()).isTrue(); TestObservationRegistryAssert.assertThat(observationRegistry) .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.client") - .hasLowCardinalityKeyValue("grpc.status_code", "CANCELLED")); + .hasLowCardinalityKeyValue("grpc.status_code", "CANCELLED")) + .hasAnObservation(observationContextAssert -> observationContextAssert.hasNameEqualTo("grpc.server") + .satisfies(observation -> assertThat(observation).isInstanceOfSatisfying( + GrpcServerObservationContext.class, + context -> assertThat(context.isCancelled()).isTrue()))); assertThat(serverHandler.getEvents()).contains(GrpcServerEvents.CANCELLED); } @@ -744,6 +752,8 @@ static class ContextAndEventHoldingObservationHandler contextClass; + private boolean contextStopped; + ContextAndEventHoldingObservationHandler(Class contextClass) { this.contextClass = contextClass; } @@ -767,10 +777,21 @@ T getContext() { return this.contextHolder.get(); } + @Override + public void onStop(T context) { + if (context.equals(this.contextHolder.get())) { + this.contextStopped = true; + } + } + List getEvents() { return this.events; } + public boolean isContextStopped() { + return this.contextStopped; + } + } static class ClientHeaderInterceptor implements ClientInterceptor {