Auto-configure Micrometer's Jersey 2 server instrumentation
See gh-12482 Co-authored-by: Michael J. Simons <michael@simons.ac>
This commit is contained in:
committed by
Andy Wilkinson
parent
f2e4a0b44b
commit
dd126faf5a
@@ -92,6 +92,11 @@
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-jersey2</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-atlas</artifactId>
|
||||
|
||||
@@ -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 extends Annotation> A findAnnotation(
|
||||
AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||
return AnnotationUtils.findAnnotation(annotatedElement,
|
||||
annotationType);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -61,6 +61,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetri
|
||||
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,\
|
||||
|
||||
@@ -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<Tag> httpRequestTags(RequestEvent event) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Tag> httpLongRequestTags(RequestEvent event) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1832,6 +1832,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<Person> 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`.
|
||||
|
||||
Reference in New Issue
Block a user