Apply user-provided ObservationConventions in auto-configurations
Prior to this commit, we would advise developers, as migration path from Spring Boot 2.0-x metrics, to create `GlobalObservationConvention` beans for the observations they want to customize (observation name or key values). `GlobalObservationConvention` are currently applied **in addition** to the chosen convention in some cases, so this does not work well with this migration path. Instead, instrumentations always provide a default convention but also a way to configure a custom convention for their observations. Spring Boot should inject custom convention beans in the relevant auto-configurations. Fixes gh-33285
This commit is contained in:
parent
f6ac891cc1
commit
07766c436c
|
@ -20,6 +20,7 @@ import graphql.GraphQL;
|
|||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
@ -28,6 +29,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.observation.DataFetcherObservationConvention;
|
||||
import org.springframework.graphql.observation.ExecutionRequestObservationConvention;
|
||||
import org.springframework.graphql.observation.GraphQlObservationInstrumentation;
|
||||
|
||||
/**
|
||||
|
@ -45,9 +48,11 @@ public class GraphQlObservationAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlObservationInstrumentation graphQlObservationInstrumentation(
|
||||
ObservationRegistry observationRegistry) {
|
||||
return new GraphQlObservationInstrumentation(observationRegistry);
|
||||
public GraphQlObservationInstrumentation graphQlObservationInstrumentation(ObservationRegistry observationRegistry,
|
||||
ObjectProvider<ExecutionRequestObservationConvention> executionConvention,
|
||||
ObjectProvider<DataFetcherObservationConvention> dataFetcherConvention) {
|
||||
return new GraphQlObservationInstrumentation(observationRegistry, executionConvention.getIfAvailable(),
|
||||
dataFetcherConvention.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,16 +45,34 @@ class RestTemplateObservationConfiguration {
|
|||
|
||||
@Bean
|
||||
ObservationRestTemplateCustomizer observationRestTemplateCustomizer(ObservationRegistry observationRegistry,
|
||||
ObjectProvider<ClientRequestObservationConvention> customConvention,
|
||||
ObservationProperties observationProperties, MetricsProperties metricsProperties,
|
||||
ObjectProvider<RestTemplateExchangeTagsProvider> optionalTagsProvider) {
|
||||
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
|
||||
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
|
||||
String name = (observationName != null) ? observationName : metricName;
|
||||
RestTemplateExchangeTagsProvider tagsProvider = optionalTagsProvider.getIfAvailable();
|
||||
ClientRequestObservationConvention observationConvention = (tagsProvider != null)
|
||||
? new ClientHttpObservationConventionAdapter(name, tagsProvider)
|
||||
: new DefaultClientRequestObservationConvention(name);
|
||||
String name = observationName(observationProperties, metricsProperties);
|
||||
ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
|
||||
name, optionalTagsProvider.getIfAvailable());
|
||||
return new ObservationRestTemplateCustomizer(observationRegistry, observationConvention);
|
||||
}
|
||||
|
||||
private static String observationName(ObservationProperties observationProperties,
|
||||
MetricsProperties metricsProperties) {
|
||||
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
|
||||
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
|
||||
return (observationName != null) ? observationName : metricName;
|
||||
}
|
||||
|
||||
private static ClientRequestObservationConvention createConvention(
|
||||
ClientRequestObservationConvention customConvention, String name,
|
||||
RestTemplateExchangeTagsProvider tagsProvider) {
|
||||
if (customConvention != null) {
|
||||
return customConvention;
|
||||
}
|
||||
else if (tagsProvider != null) {
|
||||
return new ClientHttpObservationConventionAdapter(name, tagsProvider);
|
||||
}
|
||||
else {
|
||||
return new DefaultClientRequestObservationConvention(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,16 +42,34 @@ class WebClientObservationConfiguration {
|
|||
|
||||
@Bean
|
||||
ObservationWebClientCustomizer observationWebClientCustomizer(ObservationRegistry observationRegistry,
|
||||
ObservationProperties observationProperties,
|
||||
ObjectProvider<WebClientExchangeTagsProvider> optionalTagsProvider, MetricsProperties metricsProperties) {
|
||||
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
|
||||
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
|
||||
String name = (observationName != null) ? observationName : metricName;
|
||||
WebClientExchangeTagsProvider tagsProvider = optionalTagsProvider.getIfAvailable();
|
||||
ClientRequestObservationConvention observationConvention = (tagsProvider != null)
|
||||
? new ClientObservationConventionAdapter(name, tagsProvider)
|
||||
: new DefaultClientRequestObservationConvention(name);
|
||||
ObjectProvider<ClientRequestObservationConvention> customConvention,
|
||||
ObservationProperties observationProperties, ObjectProvider<WebClientExchangeTagsProvider> tagsProvider,
|
||||
MetricsProperties metricsProperties) {
|
||||
String name = observationName(observationProperties, metricsProperties);
|
||||
ClientRequestObservationConvention observationConvention = createConvention(customConvention.getIfAvailable(),
|
||||
tagsProvider.getIfAvailable(), name);
|
||||
return new ObservationWebClientCustomizer(observationRegistry, observationConvention);
|
||||
}
|
||||
|
||||
private static ClientRequestObservationConvention createConvention(
|
||||
ClientRequestObservationConvention customConvention, WebClientExchangeTagsProvider tagsProvider,
|
||||
String name) {
|
||||
if (customConvention != null) {
|
||||
return customConvention;
|
||||
}
|
||||
else if (tagsProvider != null) {
|
||||
return new ClientObservationConventionAdapter(name, tagsProvider);
|
||||
}
|
||||
else {
|
||||
return new DefaultClientRequestObservationConvention(name);
|
||||
}
|
||||
}
|
||||
|
||||
private static String observationName(ObservationProperties observationProperties,
|
||||
MetricsProperties metricsProperties) {
|
||||
String metricName = metricsProperties.getWeb().getClient().getRequest().getMetricName();
|
||||
String observationName = observationProperties.getHttp().getClient().getRequests().getName();
|
||||
return (observationName != null) ? observationName : metricName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ public class WebFluxObservationAutoConfiguration {
|
|||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerHttpObservationFilter webfluxObservationFilter(ObservationRegistry registry,
|
||||
ObjectProvider<ServerRequestObservationConvention> customConvention,
|
||||
ObjectProvider<WebFluxTagsProvider> tagConfigurer,
|
||||
ObjectProvider<WebFluxTagsContributor> contributorsProvider) {
|
||||
String observationName = this.observationProperties.getHttp().getServer().getRequests().getName();
|
||||
|
@ -85,12 +86,17 @@ public class WebFluxObservationAutoConfiguration {
|
|||
String name = (observationName != null) ? observationName : metricName;
|
||||
WebFluxTagsProvider tagsProvider = tagConfigurer.getIfAvailable();
|
||||
List<WebFluxTagsContributor> tagsContributors = contributorsProvider.orderedStream().toList();
|
||||
ServerRequestObservationConvention convention = createConvention(name, tagsProvider, tagsContributors);
|
||||
ServerRequestObservationConvention convention = createConvention(customConvention.getIfAvailable(), name,
|
||||
tagsProvider, tagsContributors);
|
||||
return new ServerHttpObservationFilter(registry, convention);
|
||||
}
|
||||
|
||||
private ServerRequestObservationConvention createConvention(String name, WebFluxTagsProvider tagsProvider,
|
||||
private static ServerRequestObservationConvention createConvention(
|
||||
ServerRequestObservationConvention customConvention, String name, WebFluxTagsProvider tagsProvider,
|
||||
List<WebFluxTagsContributor> tagsContributors) {
|
||||
if (customConvention != null) {
|
||||
return customConvention;
|
||||
}
|
||||
if (tagsProvider != null) {
|
||||
return new ServerRequestObservationConventionAdapter(name, tagsProvider);
|
||||
}
|
||||
|
|
|
@ -82,15 +82,12 @@ public class WebMvcObservationAutoConfiguration {
|
|||
@Bean
|
||||
@ConditionalOnMissingFilterBean
|
||||
public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
|
||||
ObjectProvider<ServerRequestObservationConvention> customConvention,
|
||||
ObjectProvider<WebMvcTagsProvider> customTagsProvider,
|
||||
ObjectProvider<WebMvcTagsContributor> contributorsProvider) {
|
||||
String name = httpRequestsMetricName(this.observationProperties, this.metricsProperties);
|
||||
ServerRequestObservationConvention convention = new DefaultServerRequestObservationConvention(name);
|
||||
WebMvcTagsProvider tagsProvider = customTagsProvider.getIfAvailable();
|
||||
List<WebMvcTagsContributor> contributors = contributorsProvider.orderedStream().toList();
|
||||
if (tagsProvider != null || contributors.size() > 0) {
|
||||
convention = new ServerRequestObservationConventionAdapter(name, tagsProvider, contributors);
|
||||
}
|
||||
ServerRequestObservationConvention convention = createConvention(customConvention.getIfAvailable(), name,
|
||||
customTagsProvider.getIfAvailable(), contributorsProvider.orderedStream().toList());
|
||||
ServerHttpObservationFilter filter = new ServerHttpObservationFilter(registry, convention);
|
||||
FilterRegistrationBean<ServerHttpObservationFilter> registration = new FilterRegistrationBean<>(filter);
|
||||
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
|
||||
|
@ -98,6 +95,20 @@ public class WebMvcObservationAutoConfiguration {
|
|||
return registration;
|
||||
}
|
||||
|
||||
private static ServerRequestObservationConvention createConvention(
|
||||
ServerRequestObservationConvention customConvention, String name, WebMvcTagsProvider tagsProvider,
|
||||
List<WebMvcTagsContributor> contributors) {
|
||||
if (customConvention != null) {
|
||||
return customConvention;
|
||||
}
|
||||
else if (tagsProvider != null || contributors.size() > 0) {
|
||||
return new ServerRequestObservationConventionAdapter(name, tagsProvider, contributors);
|
||||
}
|
||||
else {
|
||||
return new DefaultServerRequestObservationConvention(name);
|
||||
}
|
||||
}
|
||||
|
||||
private static String httpRequestsMetricName(ObservationProperties observationProperties,
|
||||
MetricsProperties metricsProperties) {
|
||||
String observationName = observationProperties.getHttp().getServer().getRequests().getName();
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
|
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.graphql.observation.DefaultDataFetcherObservationConvention;
|
||||
import org.springframework.graphql.observation.DefaultExecutionRequestObservationConvention;
|
||||
import org.springframework.graphql.observation.GraphQlObservationInstrumentation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -58,6 +60,18 @@ class GraphQlObservationAutoConfigurationTests {
|
|||
.hasBean("customInstrumentation"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void instrumentationUsesCustomConventionsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(CustomConventionsConfiguration.class).run((context) -> {
|
||||
GraphQlObservationInstrumentation instrumentation = context
|
||||
.getBean(GraphQlObservationInstrumentation.class);
|
||||
assertThat(instrumentation).extracting("requestObservationConvention")
|
||||
.isInstanceOf(CustomExecutionRequestObservationConvention.class);
|
||||
assertThat(instrumentation).extracting("dataFetcherObservationConvention")
|
||||
.isInstanceOf(CustomDataFetcherObservationConvention.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class InstrumentationConfiguration {
|
||||
|
||||
|
@ -68,4 +82,27 @@ class GraphQlObservationAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConventionsConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomExecutionRequestObservationConvention customExecutionConvention() {
|
||||
return new CustomExecutionRequestObservationConvention();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CustomDataFetcherObservationConvention customDataFetcherConvention() {
|
||||
return new CustomDataFetcherObservationConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomExecutionRequestObservationConvention extends DefaultExecutionRequestObservationConvention {
|
||||
|
||||
}
|
||||
|
||||
static class CustomDataFetcherObservationConvention extends DefaultDataFetcherObservationConvention {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.observation.web.client;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
@ -41,6 +42,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.observation.ClientRequestObservationContext;
|
||||
import org.springframework.http.client.observation.DefaultClientRequestObservationConvention;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
|
@ -116,6 +119,17 @@ class RestTemplateObservationConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateCreatedWithBuilderUsesCustomConvention() {
|
||||
this.contextRunner.withUserConfiguration(CustomConvention.class).run((context) -> {
|
||||
RestTemplate restTemplate = buildRestTemplate(context);
|
||||
restTemplate.getForEntity("/projects/{project}", Void.class, "spring-boot");
|
||||
TestObservationRegistry registry = context.getBean(TestObservationRegistry.class);
|
||||
TestObservationRegistryAssert.assertThat(registry).hasObservationWithNameEqualTo("http.client.requests")
|
||||
.that().hasLowCardinalityKeyValue("project", "spring-boot");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
|
||||
this.contextRunner.with(MetricsRun.simple()).withPropertyValues("management.metrics.web.client.max-uri-tags=2")
|
||||
|
@ -153,7 +167,7 @@ class RestTemplateObservationConfigurationTests {
|
|||
return restTemplate;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomTagsConfiguration {
|
||||
|
||||
@Bean
|
||||
|
@ -173,4 +187,23 @@ class RestTemplateObservationConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConventionConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomConvention customConvention() {
|
||||
return new CustomConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomConvention extends DefaultClientRequestObservationConvention {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
|
||||
return super.getLowCardinalityKeyValues(context).and("project", "spring-boot");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.observation.web.client;
|
|||
|
||||
import java.time.Duration;
|
||||
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.tck.TestObservationRegistry;
|
||||
import io.micrometer.observation.tck.TestObservationRegistryAssert;
|
||||
|
@ -41,6 +42,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.mock.http.client.reactive.MockClientHttpResponse;
|
||||
import org.springframework.web.reactive.function.client.ClientRequestObservationContext;
|
||||
import org.springframework.web.reactive.function.client.DefaultClientRequestObservationConvention;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -84,6 +87,20 @@ class WebClientObservationConfigurationTests {
|
|||
.getBeans(WebClientExchangeTagsProvider.class).hasSize(1).containsKey("customTagsProvider"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseCustomConventionIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(CustomConvention.class).run((context) -> {
|
||||
TestObservationRegistry registry = context.getBean(TestObservationRegistry.class);
|
||||
WebClient.Builder builder = context.getBean(WebClient.Builder.class);
|
||||
WebClient webClient = mockWebClient(builder);
|
||||
TestObservationRegistryAssert.assertThat(registry).doesNotHaveAnyObservation();
|
||||
webClient.get().uri("https://example.org/projects/{project}", "spring-boot").retrieve().toBodilessEntity()
|
||||
.block(Duration.ofSeconds(30));
|
||||
TestObservationRegistryAssert.assertThat(registry).hasObservationWithNameEqualTo("http.client.requests")
|
||||
.that().hasLowCardinalityKeyValue("project", "spring-boot");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
|
||||
this.contextRunner.withPropertyValues("management.metrics.web.client.max-uri-tags=2").run((context) -> {
|
||||
|
@ -141,4 +158,23 @@ class WebClientObservationConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConventionConfig {
|
||||
|
||||
@Bean
|
||||
CustomConvention customConvention() {
|
||||
return new CustomConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomConvention extends DefaultClientRequestObservationConvention {
|
||||
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(ClientRequestObservationContext context) {
|
||||
return super.getLowCardinalityKeyValues(context).and("project", "spring-boot");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.boot.test.system.CapturedOutput;
|
|||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
@ -83,6 +84,15 @@ class WebFluxObservationAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseCustomConventionWhenAvailable() {
|
||||
this.contextRunner.withUserConfiguration(CustomConventionConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(ServerHttpObservationFilter.class);
|
||||
assertThat(context).getBean(ServerHttpObservationFilter.class).extracting("observationConvention")
|
||||
.isInstanceOf(CustomConvention.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void afterMaxUrisReachedFurtherUrisAreDenied(CapturedOutput output) {
|
||||
this.contextRunner.withUserConfiguration(TestController.class)
|
||||
|
@ -183,4 +193,18 @@ class WebFluxObservationAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConventionConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomConvention customConvention() {
|
||||
return new CustomConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomConvention extends DefaultServerRequestObservationConvention {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
@ -97,6 +98,13 @@ class WebMvcObservationAutoConfigurationTests {
|
|||
.isInstanceOf(ServerRequestObservationConventionAdapter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customConventionWhenPresent() {
|
||||
this.contextRunner.withUserConfiguration(CustomConventionConfiguration.class)
|
||||
.run((context) -> assertThat(context.getBean(FilterRegistrationBean.class).getFilter())
|
||||
.extracting("observationConvention").isInstanceOf(CustomConvention.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterRegistrationHasExpectedDispatcherTypesAndOrder() {
|
||||
this.contextRunner.run((context) -> {
|
||||
|
@ -297,4 +305,18 @@ class WebMvcObservationAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConventionConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomConvention customConvention() {
|
||||
return new CustomConvention();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomConvention extends DefaultServerRequestObservationConvention {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -769,7 +769,7 @@ By default, Spring MVC related metrics are tagged with the following information
|
|||
|===
|
||||
|
||||
To add to the default tags, provide a `@Bean` that extends `DefaultServerRequestObservationConvention` from the `org.springframework.http.observation` package.
|
||||
To replace the default tags, provide a `@Bean` that implements `GlobalObservationConvention<ServerRequestObservationContext>`.
|
||||
To replace the default tags, provide a `@Bean` that implements `ServerRequestObservationConvention`.
|
||||
|
||||
|
||||
TIP: In some cases, exceptions handled in web controllers are not recorded as request metrics tags.
|
||||
|
@ -809,7 +809,7 @@ By default, WebFlux related metrics are tagged with the following information:
|
|||
|===
|
||||
|
||||
To add to the default tags, provide a `@Bean` that extends `DefaultServerRequestObservationConvention` from the `org.springframework.http.observation.reactive` package.
|
||||
To replace the default tags, provide a `@Bean` that implements `GlobalObservationConvention<ServerRequestObservationContext>`.
|
||||
To replace the default tags, provide a `@Bean` that implements `ServerRequestObservationConvention`.
|
||||
|
||||
TIP: In some cases, exceptions handled in controllers and handler functions are not recorded as request metrics tags.
|
||||
Applications can opt in and record exceptions by <<web#web.reactive.webflux.error-handling, setting handled exceptions as request attributes>>.
|
||||
|
|
Loading…
Reference in New Issue