From 10173319ff3d1d0cacc69c798bb6623069f37a07 Mon Sep 17 00:00:00 2001 From: Bruno Marques Date: Thu, 15 Aug 2024 17:19:37 +0100 Subject: [PATCH] fix: changes server exception handler order --- .../grpc/common/util/InterceptorOrder.java | 6 +- .../advice/GrpcAdviceWithMetricsTest.java | 112 ++++++++++++++++++ 2 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/advice/GrpcAdviceWithMetricsTest.java diff --git a/grpc-common-spring-boot/src/main/java/net/devh/boot/grpc/common/util/InterceptorOrder.java b/grpc-common-spring-boot/src/main/java/net/devh/boot/grpc/common/util/InterceptorOrder.java index 938366a44..29c95dfac 100644 --- a/grpc-common-spring-boot/src/main/java/net/devh/boot/grpc/common/util/InterceptorOrder.java +++ b/grpc-common-spring-boot/src/main/java/net/devh/boot/grpc/common/util/InterceptorOrder.java @@ -45,7 +45,7 @@ public final class InterceptorOrder { /** * The order value for global exception handling interceptors. */ - public static final int ORDER_GLOBAL_EXCEPTION_HANDLING = 0; + public static final int ORDER_GLOBAL_EXCEPTION_HANDLING = 3000; /** * The order value for tracing and metrics collecting interceptors. */ @@ -68,6 +68,8 @@ public final class InterceptorOrder { */ public static final int ORDER_LAST = Ordered.LOWEST_PRECEDENCE; + private InterceptorOrder() {} + /** * Creates a new Comparator that takes {@link Order} annotations on bean factory methods into account. * @@ -101,6 +103,4 @@ public static Comparator beanFactoryAwareOrderComparator(final Applicati }); } - private InterceptorOrder() {} - } diff --git a/tests/src/test/java/net/devh/boot/grpc/test/advice/GrpcAdviceWithMetricsTest.java b/tests/src/test/java/net/devh/boot/grpc/test/advice/GrpcAdviceWithMetricsTest.java new file mode 100644 index 000000000..23bc50f93 --- /dev/null +++ b/tests/src/test/java/net/devh/boot/grpc/test/advice/GrpcAdviceWithMetricsTest.java @@ -0,0 +1,112 @@ + +package net.devh.boot.grpc.test.advice; + +import static io.grpc.Status.INVALID_ARGUMENT; +import static io.micrometer.core.instrument.binder.grpc.GrpcObservationDocumentation.LowCardinalityKeyNames.STATUS_CODE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import com.google.protobuf.Empty; + +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import io.micrometer.core.instrument.MeterRegistry; +import net.devh.boot.grpc.client.inject.GrpcClient; +import net.devh.boot.grpc.server.advice.GrpcAdvice; +import net.devh.boot.grpc.server.advice.GrpcExceptionHandler; +import net.devh.boot.grpc.server.autoconfigure.GrpcAdviceAutoConfiguration; +import net.devh.boot.grpc.server.autoconfigure.GrpcServerMetricAutoConfiguration; +import net.devh.boot.grpc.server.autoconfigure.GrpcServerMicrometerTraceAutoConfiguration; +import net.devh.boot.grpc.server.service.GrpcService; +import net.devh.boot.grpc.test.config.BaseAutoConfiguration; +import net.devh.boot.grpc.test.config.InProcessConfiguration; +import net.devh.boot.grpc.test.proto.TestServiceGrpc; + + + +@SpringBootTest +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +@SpringJUnitConfig(classes = { + InProcessConfiguration.class, + BaseAutoConfiguration.class, + GrpcAdviceWithMetricsTest.TestConfig.class +}) +@ImportAutoConfiguration(classes = {GrpcAdviceAutoConfiguration.class, + GrpcServerMetricAutoConfiguration.class, + GrpcServerMicrometerTraceAutoConfiguration.class}) +@AutoConfigureObservability +@DirtiesContext +class GrpcAdviceWithMetricsTest { + + @GrpcClient("test") + protected TestServiceGrpc.TestServiceBlockingStub blockingStub; + + @Autowired + private MeterRegistry meterRegistry; + + public static Stream metricsFlavourProvider() { + return Stream.of( + Arguments.of("grpc.server.processing.duration", "statusCode"), + Arguments.of("grpc.server", STATUS_CODE.asString())); + } + + @BeforeEach + public void setUp() { + meterRegistry.clear(); + } + + @ParameterizedTest + @MethodSource("metricsFlavourProvider") + void shouldRegisterMetricsStatusCodeWhenUsingGrpcAdvice(String metricName, String statusCodeTagName) { + var exception = assertThrows(StatusRuntimeException.class, () -> { + blockingStub.error(Empty.getDefaultInstance()); + }); + assertEquals(INVALID_ARGUMENT, exception.getStatus()); + + var meter = Optional.ofNullable(meterRegistry.find(metricName) + .tags(statusCodeTagName, Status.Code.INVALID_ARGUMENT.name()).timer()) + .orElseGet(() -> fail("expected meter with statusCode to be registered")); + + assertEquals(1L, meter.count()); + } + + + + @TestConfiguration + static class TestConfig { + + @GrpcAdvice + public static class ExceptionHandler { + @GrpcExceptionHandler + public Status handleAnyException(Exception e) { + return INVALID_ARGUMENT.withCause(e); + } + } + + @GrpcService + public static class TestGrpcAdviceService extends TestServiceGrpc.TestServiceImplBase { + @Override + public void error(Empty request, StreamObserver responseObserver) { + throw new RuntimeException("a simulated error"); + } + } + } +}