Skip to content

Auto-configure Jersey2 server instrumentation #12482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}));
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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,\
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}

}
5 changes: 5 additions & 0 deletions spring-boot-project/spring-boot-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,11 @@
<artifactId>micrometer-core</artifactId>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-jersey2</artifactId>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-atlas</artifactId>
Expand Down
Loading