From 3a565e4e4b38e37231dfbd70b2da40454b14de09 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 20 Feb 2024 11:37:09 +0100 Subject: [PATCH] Use Jersey's ObservationRequestEventListener for Jersey observability Closes gh-39633 --- .../JerseyServerMetricsAutoConfiguration.java | 80 +++-------------- ...eyServerMetricsAutoConfigurationTests.java | 90 ++----------------- .../src/docs/asciidoc/actuator/metrics.adoc | 2 +- 3 files changed, 19 insertions(+), 153 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java index 6615a69c900c..b37b87c0080d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfiguration.java @@ -16,35 +16,25 @@ package org.springframework.boot.actuate.autoconfigure.metrics.jersey; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; - -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.config.MeterFilter; -import org.glassfish.jersey.micrometer.server.AnnotationFinder; -import org.glassfish.jersey.micrometer.server.DefaultJerseyTagsProvider; -import org.glassfish.jersey.micrometer.server.JerseyTagsProvider; -import org.glassfish.jersey.micrometer.server.MetricsApplicationEventListener; +import io.micrometer.observation.ObservationRegistry; +import org.glassfish.jersey.micrometer.server.JerseyObservationConvention; +import org.glassfish.jersey.micrometer.server.ObservationApplicationEventListener; import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.server.monitoring.RequestEvent; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; import org.springframework.boot.actuate.autoconfigure.metrics.OnlyOnceLoggingDenyMeterFilter; -import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; /** @@ -53,13 +43,14 @@ * @author Michael Weirauch * @author Michael Simons * @author Andy Wilkinson + * @author Moritz Halbritter * @since 2.1.0 */ -@AutoConfiguration(after = { MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class }) +@AutoConfiguration(after = { ObservationAutoConfiguration.class }) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) -@ConditionalOnClass({ ResourceConfig.class, MetricsApplicationEventListener.class }) -@ConditionalOnBean({ MeterRegistry.class, ResourceConfig.class }) -@EnableConfigurationProperties(MetricsProperties.class) +@ConditionalOnClass({ ResourceConfig.class, ObservationApplicationEventListener.class }) +@ConditionalOnBean({ ResourceConfig.class, ObservationRegistry.class }) +@EnableConfigurationProperties({ MetricsProperties.class, ObservationProperties.class }) public class JerseyServerMetricsAutoConfiguration { private final ObservationProperties observationProperties; @@ -69,22 +60,11 @@ public JerseyServerMetricsAutoConfiguration(ObservationProperties observationPro } @Bean - @SuppressWarnings("deprecation") - @ConditionalOnMissingBean({ JerseyTagsProvider.class, - io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider.class }) - public DefaultJerseyTagsProvider jerseyTagsProvider() { - return new DefaultJerseyTagsProvider(); - } - - @Bean - @SuppressWarnings("deprecation") - public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(MeterRegistry meterRegistry, - ObjectProvider tagsProvider, - ObjectProvider micrometerTagsProvider) { + ResourceConfigCustomizer jerseyServerObservationResourceConfigCustomizer(ObservationRegistry observationRegistry, + ObjectProvider jerseyObservationConvention) { String metricName = this.observationProperties.getHttp().getServer().getRequests().getName(); - return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, - tagsProvider.getIfAvailable(() -> new JerseyTagsProviderAdapter(micrometerTagsProvider.getObject())), - metricName, true, new AnnotationUtilsAnnotationFinder())); + return (config) -> config.register(new ObservationApplicationEventListener(observationRegistry, metricName, + jerseyObservationConvention.getIfAvailable())); } @Bean @@ -97,38 +77,4 @@ public MeterFilter jerseyMetricsUriTagFilter(MetricsProperties metricsProperties metricsProperties.getWeb().getServer().getMaxUriTags(), filter); } - /** - * An {@link AnnotationFinder} that uses {@link AnnotationUtils}. - */ - private static final class AnnotationUtilsAnnotationFinder implements AnnotationFinder { - - @Override - public A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { - return AnnotationUtils.findAnnotation(annotatedElement, annotationType); - } - - } - - @SuppressWarnings("deprecation") - static final class JerseyTagsProviderAdapter implements JerseyTagsProvider { - - private final io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider delegate; - - private JerseyTagsProviderAdapter( - io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider delegate) { - this.delegate = delegate; - } - - @Override - public Iterable httpRequestTags(RequestEvent event) { - return this.delegate.httpRequestTags(event); - } - - @Override - public Iterable httpLongRequestTags(RequestEvent event) { - return this.delegate.httpLongRequestTags(event); - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfigurationTests.java index 612db1b2b514..e117e02359d0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey/JerseyServerMetricsAutoConfigurationTests.java @@ -17,25 +17,18 @@ package org.springframework.boot.actuate.autoconfigure.metrics.jersey; import java.net.URI; -import java.util.Set; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.glassfish.jersey.micrometer.server.DefaultJerseyTagsProvider; -import org.glassfish.jersey.micrometer.server.JerseyTagsProvider; -import org.glassfish.jersey.micrometer.server.MetricsApplicationEventListener; +import org.glassfish.jersey.micrometer.server.ObservationApplicationEventListener; import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.server.monitoring.RequestEvent; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration.JerseyTagsProviderAdapter; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -58,6 +51,7 @@ * * @author Michael Weirauch * @author Michael Simons + * @author Moritz Halbritter */ class JerseyServerMetricsAutoConfigurationTests { @@ -80,32 +74,10 @@ void shouldOnlyBeActiveInWebApplicationContext() { @Test void shouldProvideAllNecessaryBeans() { - this.webContextRunner.run((context) -> assertThat(context).hasSingleBean(DefaultJerseyTagsProvider.class) + this.webContextRunner.run((context) -> assertThat(context).hasBean("jerseyMetricsUriTagFilter") .hasSingleBean(ResourceConfigCustomizer.class)); } - @Test - void shouldHonorExistingTagProvider() { - this.webContextRunner.withUserConfiguration(CustomJerseyTagsProviderConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(CustomJerseyTagsProvider.class)); - } - - @Test - @Deprecated(since = "3.3.0", forRemoval = true) - void shouldHonorExistingMicrometerTagProvider() { - this.webContextRunner.withUserConfiguration(CustomMicrometerJerseyTagsProviderConfiguration.class) - .run((context) -> { - assertThat(context).hasSingleBean(CustomMicrometerJerseyTagsProvider.class); - ResourceConfig config = new ResourceConfig(); - context.getBean(ResourceConfigCustomizer.class).customize(config); - Set instances = config.getInstances(); - assertThat(instances).hasSize(1) - .first(InstanceOfAssertFactories.type(MetricsApplicationEventListener.class)) - .satisfies((listener) -> assertThat(listener).extracting("tagsProvider") - .isInstanceOf(JerseyTagsProviderAdapter.class)); - }); - } - @Test void httpRequestsAreTimed() { this.webContextRunner.run((context) -> { @@ -118,10 +90,9 @@ void httpRequestsAreTimed() { @Test void noHttpRequestsTimedWhenJerseyInstrumentationMissingFromClasspath() { - this.webContextRunner.withClassLoader(new FilteredClassLoader(MetricsApplicationEventListener.class)) + this.webContextRunner.withClassLoader(new FilteredClassLoader(ObservationApplicationEventListener.class)) .run((context) -> { doRequest(context); - MeterRegistry registry = context.getBean(MeterRegistry.class); assertThat(registry.find("http.server.requests").timer()).isNull(); }); @@ -144,7 +115,7 @@ ResourceConfig resourceConfig() { } @Path("/users") - public class TestResource { + public static class TestResource { @GET @Path("/{id}") @@ -156,55 +127,4 @@ public String getUser(@PathParam("id") String id) { } - @Configuration(proxyBeanMethods = false) - static class CustomJerseyTagsProviderConfiguration { - - @Bean - JerseyTagsProvider customJerseyTagsProvider() { - return new CustomJerseyTagsProvider(); - } - - } - - static class CustomJerseyTagsProvider implements JerseyTagsProvider { - - @Override - public Iterable httpRequestTags(RequestEvent event) { - return null; - } - - @Override - public Iterable httpLongRequestTags(RequestEvent event) { - return null; - } - - } - - @SuppressWarnings("deprecation") - @Configuration(proxyBeanMethods = false) - static class CustomMicrometerJerseyTagsProviderConfiguration { - - @Bean - io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider customJerseyTagsProvider() { - return new CustomMicrometerJerseyTagsProvider(); - } - - } - - @SuppressWarnings("deprecation") - static class CustomMicrometerJerseyTagsProvider - implements io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider { - - @Override - public Iterable httpRequestTags(RequestEvent event) { - return null; - } - - @Override - public Iterable httpLongRequestTags(RequestEvent event) { - return null; - } - - } - } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc index 184f8c80eb92..8ea7054c6b24 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc @@ -815,7 +815,7 @@ By default, Jersey server metrics are tagged with the following information: | The request's URI template prior to variable substitution, if possible (for example, `/api/person/\{id}`) |=== -To customize the tags, provide a `@Bean` that implements `JerseyTagsProvider`. +To customize the tags, provide a `@Bean` that implements `JerseyObservationConvention`.