Use Jersey's ObservationRequestEventListener for Jersey observability

Closes gh-39633
This commit is contained in:
Moritz Halbritter
2024-02-20 11:37:09 +01:00
parent 7e43d7ba0f
commit 3a565e4e4b
3 changed files with 19 additions and 153 deletions

View File

@@ -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 @@ import org.springframework.core.annotation.Order;
* @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 class JerseyServerMetricsAutoConfiguration {
}
@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<JerseyTagsProvider> tagsProvider,
ObjectProvider<io.micrometer.core.instrument.binder.jersey.server.JerseyTagsProvider> micrometerTagsProvider) {
ResourceConfigCustomizer jerseyServerObservationResourceConfigCustomizer(ObservationRegistry observationRegistry,
ObjectProvider<JerseyObservationConvention> 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 class JerseyServerMetricsAutoConfiguration {
metricsProperties.getWeb().getServer().getMaxUriTags(), filter);
}
/**
* An {@link AnnotationFinder} that uses {@link AnnotationUtils}.
*/
private static final class AnnotationUtilsAnnotationFinder implements AnnotationFinder {
@Override
public <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> 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<Tag> httpRequestTags(RequestEvent event) {
return this.delegate.httpRequestTags(event);
}
@Override
public Iterable<Tag> httpLongRequestTags(RequestEvent event) {
return this.delegate.httpLongRequestTags(event);
}
}
}

View File

@@ -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 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Michael Weirauch
* @author Michael Simons
* @author Moritz Halbritter
*/
class JerseyServerMetricsAutoConfigurationTests {
@@ -80,32 +74,10 @@ class JerseyServerMetricsAutoConfigurationTests {
@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<Object> 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 @@ class JerseyServerMetricsAutoConfigurationTests {
@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 @@ class JerseyServerMetricsAutoConfigurationTests {
}
@Path("/users")
public class TestResource {
public static class TestResource {
@GET
@Path("/{id}")
@@ -156,55 +127,4 @@ class JerseyServerMetricsAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
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;
}
}
@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<Tag> httpRequestTags(RequestEvent event) {
return null;
}
@Override
public Iterable<Tag> httpLongRequestTags(RequestEvent event) {
return null;
}
}
}

View File

@@ -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`.