diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml index f8c2c72029ab..ac15bb60310d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml @@ -92,6 +92,11 @@ micrometer-core true + + io.micrometer + micrometer-jersey2 + true + io.micrometer micrometer-registry-atlas diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfiguration.java new file mode 100644 index 000000000000..59ffcc93cfc2 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfiguration.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.metrics.web.jersey; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.jersey2.server.AnnotationFinder; +import io.micrometer.jersey2.server.DefaultJerseyTagsProvider; +import io.micrometer.jersey2.server.JerseyTagsProvider; +import io.micrometer.jersey2.server.MetricsApplicationEventListener; +import org.glassfish.jersey.server.ResourceConfig; + +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +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.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for instrumentation of Jersey servlet-based request mappings. + * + * @author Michael Simons + * @since 2.1.0 + */ +@Configuration +@AutoConfigureAfter({ MetricsAutoConfiguration.class, + SimpleMetricsExportAutoConfiguration.class }) +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) +@ConditionalOnClass({ResourceConfig.class, MetricsApplicationEventListener.class}) +@ConditionalOnBean({MeterRegistry.class, ResourceConfig.class}) +@ConditionalOnMissingBean(type = "org.springframework.web.servlet.DispatcherServlet") +@EnableConfigurationProperties(MetricsProperties.class) +public class JerseyServerMetricsAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(JerseyTagsProvider.class) + public DefaultJerseyTagsProvider jerseyTagsProvider() { + return new DefaultJerseyTagsProvider(); + } + + @Bean + public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer( + final MeterRegistry meterRegistry, + final MetricsProperties properties, + final JerseyTagsProvider tagsProvider) { + final MetricsProperties.Web.Server serverProperties = properties.getWeb().getServer(); + return (config) -> config.register(new MetricsApplicationEventListener(meterRegistry, tagsProvider, + serverProperties.getRequestsMetricName(), serverProperties.isAutoTimeRequests(), + new AnnotationFinder() { + @Override + public A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { + return AnnotationUtils.findAnnotation(annotatedElement, annotationType); + } + })); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/package-info.java new file mode 100644 index 000000000000..77ff7c6ff263 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Auto-configuration for Jersey / JAX-RS actuator metrics. + */ +package org.springframework.boot.actuate.autoconfigure.metrics.web.jersey; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories index 01ba105602e7..c2d75289b62d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories @@ -51,6 +51,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.Wavefron org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.client.RestTemplateMetricsAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.metrics.web.jersey.JerseyServerMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat.TomcatMetricsAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfigurationTests.java new file mode 100644 index 000000000000..6dfbc090c6f0 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jersey/JerseyServerMetricsAutoConfigurationTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.metrics.web.jersey; + +import io.micrometer.core.instrument.Tag; +import io.micrometer.jersey2.server.DefaultJerseyTagsProvider; +import io.micrometer.jersey2.server.JerseyTagsProvider; +import io.micrometer.jersey2.server.MetricsApplicationEventListener; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.monitoring.RequestEvent; +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration; +import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.context.annotation.Bean; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JerseyServerMetricsAutoConfiguration}. + * + * @author Michael Simons + */ +public class JerseyServerMetricsAutoConfigurationTests { + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .with(MetricsRun.simple()) + .withConfiguration( + AutoConfigurations.of(JerseyServerMetricsAutoConfiguration.class) + ); + + private final WebApplicationContextRunner webContextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + JerseyAutoConfiguration.class, MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class, + JerseyServerMetricsAutoConfiguration.class) + ) + .withUserConfiguration(JerseyConfig.class); + + @Test + public void shouldOnlyBeActiveInWebApplicationContext() { + this.contextRunner.run(ctx -> assertThat(ctx).doesNotHaveBean(ResourceConfigCustomizer.class)); + } + + @Test + public void shouldProvideALlNecessaryBeans() { + this.webContextRunner.run(ctx -> assertThat(ctx) + .hasSingleBean(DefaultJerseyTagsProvider.class) + .hasSingleBean(ResourceConfigCustomizer.class) + ); + } + + @Test + public void shouldHonorExistingTagProvider() { + this.webContextRunner + .withUserConfiguration(JerseyServerMetricConfig.class) + .run(ctx -> assertThat(ctx).hasSingleBean(ATagsProvider.class)); + } + + @Test + public void doesNotFailWithoutJersey2Metrics() { + this.webContextRunner + .withClassLoader(new FilteredClassLoader(MetricsApplicationEventListener.class)) + .run(ctx -> assertThat(ctx).doesNotHaveBean(ResourceConfigCustomizer.class)); + } + + static class JerseyConfig { + @Bean + ResourceConfig resourceConfig() { + return new ResourceConfig(); + } + } + + static class JerseyServerMetricConfig { + @Bean + JerseyTagsProvider jerseyTagsProvider() { + return new ATagsProvider(); + } + } + + static class ATagsProvider implements JerseyTagsProvider { + + @Override + public Iterable httpRequestTags(RequestEvent requestEvent) { + return null; + } + + @Override + public Iterable httpLongRequestTags(RequestEvent requestEvent) { + return null; + } + } +} diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index df5d29db1e7c..8422b516024d 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -861,6 +861,11 @@ micrometer-core ${micrometer.version} + + io.micrometer + micrometer-jersey2 + ${micrometer.version} + io.micrometer micrometer-registry-atlas diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index e006f6f37ddf..8067e6f92877 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -1705,6 +1705,48 @@ To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`. +[[production-ready-metrics-spring-mvc]] +==== Jersey 2 Metrics +Auto-configuration enables the instrumentation of requests handled by the Jersey 2 implementation of JAX-RS. +When `management.metrics.web.server.auto-time-requests` is `true`, this instrumentation occurs +for all requests. Alternatively, when set to `false`, you can enable instrumentation by +adding `@Timed` to a request-handling method: + +[source,java,indent=0] +---- + @Component + @Path("/api/people") + @Timed <1> + public class Endpoint { + + @GET + @Timed(extraTags = { "region", "us-east-1" }) <2> + @Timed(value = "all.people", longTask = true) <3> + public List listPeople() { ... } + } +---- +<1> On a resource class to enable timings on every request handler in the resource. +<2> On a method to enable for an individual endpoint. This is not necessary if you have it on +the class, but can be used to further customize the timer for this particular endpoint. +<3> On a method with `longTask = true` to enable a long task timer for the method. Long task +timers require a separate metric name, and can be stacked with a short task timer. + +By default, metrics are generated with the name, `http.server.requests`. The name can be +customized by setting the `management.metrics.web.server.requests-metric-name` property. + +By default, Spring MVC-related metrics are tagged with the following information: + +* `method`, the request's method (for example, `GET` or `POST`). +* `uri`, the request's URI template prior to variable substitution, if possible (for +example, `/api/person/{id}`). +* `status`, the response's HTTP status code (for example, `200` or `500`). +* `exception`, the simple class name of any exception that was thrown while handling the +request. + +To customize the tags, provide a `@Bean` that implements `JerseyTagsProvider`. + + + [[production-ready-metrics-rest-template]] ==== RestTemplate Metrics The instrumentation of any `RestTemplate` created using the auto-configured