diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
index bd2f32275f8e..887fe41914c7 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/jersey2/server/JerseyServerMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsAutoConfiguration.java
new file mode 100644
index 000000000000..431ec310d2f8
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsAutoConfiguration.java
@@ -0,0 +1,81 @@
+/*
+ * 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.jersey2.server;
+
+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.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 Jersey server instrumentation.
+ *
+ * @author Michael Weirauch
+ * @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 })
+@EnableConfigurationProperties(JerseyServerMetricsProperties.class)
+public class JerseyServerMetricsAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(JerseyTagsProvider.class)
+ public DefaultJerseyTagsProvider jerseyTagsProvider() {
+ return new DefaultJerseyTagsProvider();
+ }
+
+ @Bean
+ public ResourceConfigCustomizer jerseyServerMetricsResourceConfigCustomizer(
+ MeterRegistry meterRegistry, JerseyServerMetricsProperties properties,
+ JerseyTagsProvider tagsProvider) {
+ return (config) -> config.register(new MetricsApplicationEventListener(
+ meterRegistry, tagsProvider, properties.getRequestsMetricName(),
+ properties.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/jersey2/server/JerseyServerMetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsProperties.java
new file mode 100644
index 000000000000..8fddb1e8a446
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsProperties.java
@@ -0,0 +1,50 @@
+/*
+ * 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.jersey2.server;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Configuration for Jersey server instrumentation.
+ *
+ * @author Michael Weirauch
+ * @since 2.1.0
+ */
+@ConfigurationProperties(prefix = "management.metrics.jersey2.server")
+public class JerseyServerMetricsProperties {
+
+ private String requestsMetricName = "http.server.requests";
+
+ private boolean autoTimeRequests = true;
+
+ public String getRequestsMetricName() {
+ return this.requestsMetricName;
+ }
+
+ public void setRequestsMetricName(String requestsMetricName) {
+ this.requestsMetricName = requestsMetricName;
+ }
+
+ public boolean isAutoTimeRequests() {
+ return this.autoTimeRequests;
+ }
+
+ public void setAutoTimeRequests(boolean autoTimeRequests) {
+ this.autoTimeRequests = autoTimeRequests;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/package-info.java
new file mode 100644
index 000000000000..093d22887889
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/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 server actuator metrics.
+ */
+package org.springframework.boot.actuate.autoconfigure.metrics.jersey2.server;
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 0109c40f12fb..05ac8400334b 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
@@ -54,6 +54,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.signalfx.SignalFxM
org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration,\
+org.springframework.boot.actuate.autoconfigure.metrics.jersey2.server.JerseyServerMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.web.reactive.WebFluxMetricsAutoConfiguration,\
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsAutoConfigurationTests.java
new file mode 100644
index 000000000000..6d27eae9536c
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jersey2/server/JerseyServerMetricsAutoConfigurationTests.java
@@ -0,0 +1,174 @@
+/*
+ * 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.jersey2.server;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Timer;
+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.MetricsAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
+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.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link JerseyServerMetricsAutoConfiguration}.
+ *
+ * @author Michael Weirauch
+ * @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(
+ AnnotationConfigServletWebServerApplicationContext::new)
+ .withConfiguration(
+ AutoConfigurations.of(JerseyAutoConfiguration.class,
+ JerseyServerMetricsAutoConfiguration.class,
+ ServletWebServerFactoryAutoConfiguration.class,
+ SimpleMetricsExportAutoConfiguration.class,
+ MetricsAutoConfiguration.class))
+ .withUserConfiguration(ResourceConfiguration.class)
+ .withPropertyValues("server.port:0");
+
+ @Test
+ public void shouldOnlyBeActiveInWebApplicationContext() {
+ this.contextRunner.run((context) -> assertThat(context)
+ .doesNotHaveBean(ResourceConfigCustomizer.class));
+ }
+
+ @Test
+ public void shouldProvideAllNecessaryBeans() {
+ this.webContextRunner.run((context) -> assertThat(context)
+ .hasSingleBean(DefaultJerseyTagsProvider.class)
+ .hasSingleBean(ResourceConfigCustomizer.class));
+ }
+
+ @Test
+ public void shouldHonorExistingTagProvider() {
+ this.webContextRunner
+ .withUserConfiguration(CustomJerseyTagsProviderConfiguration.class)
+ .run((context) -> assertThat(context)
+ .hasSingleBean(CustomJerseyTagsProvider.class));
+ }
+
+ @Test
+ public void httpRequestsAreTimed() {
+ this.webContextRunner.run((context) -> {
+ doRequest(context);
+
+ MeterRegistry registry = context.getBean(MeterRegistry.class);
+ Timer timer = registry.get("http.server.requests").tag("uri", "/users/{id}")
+ .timer();
+ assertThat(timer.count()).isEqualTo(1);
+ });
+ }
+
+ @Test
+ public void noHttpRequestsTimedWhenJerseyInstrumentationMissingFromClasspath() {
+ this.webContextRunner
+ .withClassLoader(
+ new FilteredClassLoader(MetricsApplicationEventListener.class))
+ .run((context) -> {
+ doRequest(context);
+
+ MeterRegistry registry = context.getBean(MeterRegistry.class);
+ assertThat(registry.find("http.server.requests").timer()).isNull();
+ });
+ }
+
+ private static void doRequest(AssertableWebApplicationContext context) {
+ int port = context
+ .getSourceApplicationContext(
+ AnnotationConfigServletWebServerApplicationContext.class)
+ .getWebServer().getPort();
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.getForEntity(URI.create("http://localhost:" + port + "/users/3"),
+ String.class);
+ }
+
+ static class ResourceConfiguration {
+
+ @Bean
+ ResourceConfig resourceConfig() {
+ return new ResourceConfig().register(new TestResource());
+ }
+
+ @Path("/users")
+ public class TestResource {
+
+ @GET
+ @Path("/{id}")
+ public String getUser(@PathParam("id") String id) {
+ return id;
+ }
+
+ }
+
+ }
+
+ 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;
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml
index ea045f83a6b7..03b63d4b2464 100644
--- a/spring-boot-project/spring-boot-dependencies/pom.xml
+++ b/spring-boot-project/spring-boot-dependencies/pom.xml
@@ -869,6 +869,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 b11ef639d6a9..0e2148446aa0 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
@@ -1756,6 +1756,47 @@ To customize the tags, provide a `@Bean` that implements `WebFluxTagsProvider`.
+[[production-ready-metrics-jersey-server]]
+==== Jersey Server Metrics
+Auto-configuration enables the instrumentation of requests handled by the Jersey JAX-RS
+implementation. When `management.metrics.jersey2.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.jersey2.server.requests-metric-name` property.
+
+By default, Jersey server 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-http-clients]]
==== HTTP Client Metrics
Spring Boot Actuator manages the instrumentation of both `RestTemplate` and `WebClient`.