diff --git a/spring-grpc-dependencies/pom.xml b/spring-grpc-dependencies/pom.xml index dd4275e7..8c39409e 100644 --- a/spring-grpc-dependencies/pom.xml +++ b/spring-grpc-dependencies/pom.xml @@ -54,6 +54,7 @@ 1.63.2 3.25.5 2.46.0 + 1.13.6 @@ -112,6 +113,13 @@ proto-google-common-protos ${google-common-protos.version} + + io.micrometer + micrometer-bom + ${micrometer.version} + pom + import + diff --git a/spring-grpc-spring-boot-autoconfigure/pom.xml b/spring-grpc-spring-boot-autoconfigure/pom.xml index 35521a06..74f57b2a 100644 --- a/spring-grpc-spring-boot-autoconfigure/pom.xml +++ b/spring-grpc-spring-boot-autoconfigure/pom.xml @@ -75,6 +75,16 @@ spring-boot-starter provided + + io.micrometer + micrometer-observation + true + + + io.micrometer + micrometer-core + true + diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java new file mode 100644 index 00000000..37027ab6 --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java @@ -0,0 +1,32 @@ +package org.springframework.grpc.autoconfigure.server; + +import io.grpc.ServerBuilder; +import io.grpc.ServerInterceptor; +import io.micrometer.core.instrument.binder.grpc.ObservationGrpcServerInterceptor; +import io.micrometer.observation.ObservationRegistry; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.grpc.server.ServerBuilderCustomizer; + +@AutoConfiguration( + afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration") +@ConditionalOnClass(value = { ObservationRegistry.class, ObservationGrpcServerInterceptor.class }) +@ConditionalOnBean(ObservationRegistry.class) +@ConditionalOnProperty(value = "spring.grpc.server.observation.enabled", matchIfMissing = true) +public class GrpcServerObservationAutoConfiguration { + + @Bean + public ServerInterceptor observationGrpcServerInterceptor(ObservationRegistry observationRegistry) { + return new ObservationGrpcServerInterceptor(observationRegistry); + } + + @Bean + > ServerBuilderCustomizer metricsInterceptor( + ServerInterceptor observationGrpcServerInterceptor) { + return (serverBuilder) -> serverBuilder.intercept(observationGrpcServerInterceptor); + } + +} \ No newline at end of file diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java new file mode 100644 index 00000000..2282beb6 --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java @@ -0,0 +1,48 @@ +package org.springframework.grpc.autoconfigure.server; + +import io.grpc.ServerBuilder; +import io.grpc.ServerInterceptor; +import io.micrometer.observation.ObservationRegistry; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.grpc.server.ServerBuilderCustomizer; + +import static org.assertj.core.api.Assertions.assertThat; + +class GrpcServerObservationAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcServerObservationAutoConfiguration.class)); + + @Test + void whenObservationRegistryNotProvided_thenObservationInterceptorNotConfigured() { + this.contextRunner.run(context -> { + assertThat(context).doesNotHaveBean(ServerBuilderCustomizer.class); + }); + } + + @Test + void whenObservationInterceptorConfigured_thenServerBuilderCustomizerConfigured() { + this.contextRunner.withBean(ObservationRegistry.class, ObservationRegistry::create).run(context -> { + assertThat(context).hasSingleBean(ServerBuilderCustomizer.class); + assertThat(context).hasSingleBean(ServerInterceptor.class); + ServerInterceptor interceptor = context.getBean(ServerInterceptor.class); + ServerBuilderCustomizer customizer = context.getBean(ServerBuilderCustomizer.class); + ServerBuilder builder = org.mockito.Mockito.mock(ServerBuilder.class); + customizer.customize(builder); + org.mockito.Mockito.verify(builder, org.mockito.Mockito.times(1)).intercept(interceptor); + }); + } + + @Test + void whenObservationPropertyDisabled_thenServerBuilderCustomizerNotConfigured() { + this.contextRunner.withPropertyValues("spring.grpc.server.observation.enabled=false") + .withBean(ObservationRegistry.class, ObservationRegistry::create) + .run(context -> { + assertThat(context).doesNotHaveBean(ServerBuilderCustomizer.class); + assertThat(context).doesNotHaveBean(ServerInterceptor.class); + }); + } + +} \ No newline at end of file