Polish "Configure OpenTelemetry semantic conventions"

See gh-49241

Signed-off-by: Andy Wilkinson <andy.wilkinson@broadcom.com>
This commit is contained in:
Andy Wilkinson
2026-09-17 10:18:17 +01:00
parent 7d43f31732
commit 02cf5b8d0f
13 changed files with 389 additions and 52 deletions
@@ -731,7 +731,8 @@ NOTE: javadoc:org.springframework.jms.core.JmsClient[] and javadoc:org.springfra
Auto-configuration enables the instrumentation of all requests handled by Spring MVC controllers and functional handlers.
By default, metrics are generated with the name, `http.server.requests`.
You can customize the name by setting the configprop:management.observations.http.server.requests.name[] property.
If you are xref:actuator/observability.adoc#actuator.observability.semantic-conventions[using Micrometer's semantic conventions], set the configprop:management.observations.http.server.requests.name[] property to customize the name.
If you are using OpenTelemtry's semantic conventions, the name cannot be customized.
See the {url-spring-framework-docs}/integration/observability.html#observability.http-server.servlet[Spring Framework reference documentation for more information on produced observations].
@@ -751,7 +752,8 @@ To customize the filter, provide a javadoc:org.springframework.context.annotatio
Auto-configuration enables the instrumentation of all requests handled by Spring WebFlux controllers and functional handlers.
By default, metrics are generated with the name, `http.server.requests`.
You can customize the name by setting the configprop:management.observations.http.server.requests.name[] property.
If you are xref:actuator/observability.adoc#actuator.observability.semantic-conventions[using Micrometer's semantic conventions], set the configprop:management.observations.http.server.requests.name[] property to customize the name.
If you are using OpenTelemtry's semantic conventions, the name cannot be customized.
See the {url-spring-framework-docs}/integration/observability.html#observability.http-server.reactive[Spring Framework reference documentation for more information on produced observations].
@@ -101,14 +101,26 @@ In that case you can either disable the automatic instrumentation using xref:ref
[[actuator.observability.semantic-conventions]]
== Semantic Conventions
Semantic conventions govern things like metric names and tag names and values.
By default, the conventions defined by Micrometer and the various Spring projects are used.
To switch to OpenTelemetry's conventions, set configprop:management.observations.conventions[] to `open-telemetry`.
OpenTelemetry conventions are limited to those that are stable.
When using OpenTelemetry's conventions, note that certain properties for customizing meter names and the like will no longer take effect.
[[actuator.observability.opentelemetry]]
== OpenTelemetry Support
NOTE: There are several ways to support https://opentelemetry.io/[OpenTelemetry] in your application.
You can use the https://opentelemetry.io/docs/zero-code/java/agent/[OpenTelemetry Java Agent] or the https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/[OpenTelemetry Spring Boot Starter],
which are supported by the OTel community; the metrics and traces use the semantic conventions defined by OTel libraries.
This documentation describes OpenTelemetry as officially supported by the Spring team, using Micrometer and the OTLP exporter;
the metrics and traces use the semantic conventions described in the Spring projects documentation, such as {url-spring-framework-docs}/integration/observability.html[Spring Framework].
which are supported by the OTel community.
In this case, the metrics and traces always use the semantic conventions defined by OTel libraries.
This documentation describes OpenTelemetry as officially supported by the Spring team, using Micrometer and the OTLP exporter.
In this case, the metrics and traces use the semantic conventions described in the Spring projects documentation, such as {url-spring-framework-docs}/integration/observability.html[Spring Framework], but can be <<actuator.observability.semantic-conventions, configured>> to use OpenTelemetry's stable conventions.
Spring Boot's actuator module includes basic support for OpenTelemetry.
@@ -55,7 +55,7 @@ import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.micrometer.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties.ConventionsVariant;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.SemanticConventions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
@@ -126,7 +126,7 @@ public final class JvmMetricsAutoConfiguration {
return new JvmThreadMetrics(Collections.emptyList(), deprecatedConventions);
}
JvmThreadMetrics.Builder builder = JvmThreadMetrics.builder();
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
if (observationProperties.getConventions() == SemanticConventions.OPEN_TELEMETRY) {
builder.openTelemetryConventions();
}
PropertyMapper map = PropertyMapper.get();
@@ -192,7 +192,7 @@ public final class JvmMetricsAutoConfiguration {
JvmMemoryMetrics create(ObservationProperties observationProperties) {
JvmMemoryMetrics.Builder builder = JvmMemoryMetrics.builder();
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
if (observationProperties.getConventions() == SemanticConventions.OPEN_TELEMETRY) {
builder.openTelemetryConventions();
}
PropertyMapper map = PropertyMapper.get();
@@ -216,7 +216,7 @@ public final class JvmMetricsAutoConfiguration {
ClassLoaderMetrics create(ObservationProperties observationProperties) {
ClassLoaderMetrics.Builder builder = ClassLoaderMetrics.builder();
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
if (observationProperties.getConventions() == SemanticConventions.OPEN_TELEMETRY) {
builder.openTelemetryConventions();
}
PropertyMapper map = PropertyMapper.get();
@@ -44,7 +44,7 @@ import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConf
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.micrometer.metrics.system.DiskSpaceMetricsBinder;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties.ConventionsVariant;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.SemanticConventions;
import org.springframework.context.annotation.Bean;
/**
@@ -108,7 +108,7 @@ public final class SystemMetricsAutoConfiguration {
ProcessorMetrics create(ObservationProperties observationProperties) {
ProcessorMetrics.Builder builder = ProcessorMetrics.builder();
if (observationProperties.getConventions() == ConventionsVariant.OPENTELEMETRY) {
if (observationProperties.getConventions() == SemanticConventions.OPEN_TELEMETRY) {
builder.openTelemetryConventions();
}
PropertyMapper map = PropertyMapper.get();
@@ -20,6 +20,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.SemanticConventions;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring Micrometer
@@ -46,9 +47,9 @@ public class ObservationProperties {
private Map<String, Boolean> enable = new LinkedHashMap<>();
/**
* Conventions variant to use when configuring observations and metrics.
* Semantic conventions to use when configuring observations and metrics.
*/
private ConventionsVariant conventions = ConventionsVariant.MICROMETER;
private SemanticConventions conventions = SemanticConventions.MICROMETER;
public Map<String, Boolean> getEnable() {
return this.enable;
@@ -70,20 +71,14 @@ public class ObservationProperties {
this.keyValues = keyValues;
}
public ConventionsVariant getConventions() {
public SemanticConventions getConventions() {
return this.conventions;
}
public void setConventions(ConventionsVariant conventions) {
public void setConventions(SemanticConventions conventions) {
this.conventions = conventions;
}
public enum ConventionsVariant {
OPENTELEMETRY, MICROMETER,
}
public static class Http {
private final Client client = new Client();
@@ -0,0 +1,46 @@
/*
* Copyright 2012-present 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
*
* https://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.micrometer.observation.autoconfigure.condition;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional @Conditional} that matches when the specified semantic conventions
* are enabled.
*
* @author Andy Wilkinson
* @since 4.1.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSemanticConventionsCondition.class)
public @interface ConditionalOnSemanticConventions {
/**
* The semantic conventions that must be enabled for the condition to match.
* @return the conventions
*/
SemanticConventions value();
}
@@ -0,0 +1,59 @@
/*
* Copyright 2012-present 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
*
* https://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.micrometer.observation.autoconfigure.condition;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
/**
* {@link Condition} that checks for particular {@link SemanticConventions}.
*
* @author Andy Wilkinson
* @see ConditionalOnSemanticConventions
*/
class OnSemanticConventionsCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, @Nullable Object> attributes = metadata
.getAnnotationAttributes(ConditionalOnSemanticConventions.class.getName());
Assert.state(attributes != null, "'attributes' must not be null");
SemanticConventions semanticConvention = (SemanticConventions) attributes.get("value");
Assert.state(semanticConvention != null, "'semanticConvention' must not be null");
return getMatchOutcome(context.getEnvironment(), semanticConvention);
}
private ConditionOutcome getMatchOutcome(Environment environment, SemanticConventions semanticConvention) {
String name = semanticConvention.name();
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnSemanticConventions.class);
if (semanticConvention.isActive(environment)) {
return ConditionOutcome.match(message.foundExactly(name));
}
return ConditionOutcome.noMatch(message.didNotFind(name).atAll());
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2012-present 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
*
* https://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.micrometer.observation.autoconfigure.condition;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.Environment;
/**
* Enumeration of supported semantic conventions.
*
* @author Andy Wilkinson
* @since 4.2.0
*/
public enum SemanticConventions {
/**
* Micrometer semantic conventions.
*/
MICROMETER(true),
/**
* OpenTelemetry semantic conventions.
*/
OPEN_TELEMETRY;
private final boolean matchIfMissing;
SemanticConventions() {
this(false);
}
SemanticConventions(boolean matchIfMissing) {
this.matchIfMissing = matchIfMissing;
}
boolean isActive(Environment environment) {
BindResult<String> result = Binder.get(environment, null)
.bind("management.observations.conventions", String.class);
if (!result.isBound()) {
return this.matchIfMissing;
}
return name().equalsIgnoreCase(result.get()) || getCanonicalName().equalsIgnoreCase(result.get());
}
private String getCanonicalName() {
String name = name();
StringBuilder canonicalName = new StringBuilder(name.length());
name.chars()
.filter(Character::isLetterOrDigit)
.map(Character::toLowerCase)
.forEach((c) -> canonicalName.append((char) c));
return canonicalName.toString();
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2012-present 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
*
* https://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.
*/
/**
* Conditions related to auto-configuration for Micrometer Observation.
*/
@NullMarked
package org.springframework.boot.micrometer.observation.autoconfigure.condition;
import org.jspecify.annotations.NullMarked;
@@ -0,0 +1,66 @@
/*
* Copyright 2012-present 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
*
* https://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.micrometer.observation.autoconfigure.condition;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ConditionalOnSemanticConventions}.
*
* @author Andy Wilkinson
*/
class ConditionalOnSemanticConventionsTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(Micrometer.class, Otel.class);
@Test
void whenPropertyIsNotSetOnlyMicrometerIsActive() {
this.contextRunner
.run((context) -> assertThat(context).hasSingleBean(Micrometer.class).doesNotHaveBean(Otel.class));
}
@Test
void whenPropertyIsSetToOpenTelemetryOnlyOpenTelementryIsActive() {
this.contextRunner.withPropertyValues("management.observations.conventions=opentelemetry")
.run((context) -> assertThat(context).hasSingleBean(Otel.class).doesNotHaveBean(Micrometer.class));
}
@Test
void whenPropertyIsSetToMicrometerOnlyMicrometerIsActive() {
this.contextRunner.withPropertyValues("management.observations.conventions=micrometer")
.run((context) -> assertThat(context).hasSingleBean(Micrometer.class).doesNotHaveBean(Otel.class));
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnSemanticConventions(SemanticConventions.MICROMETER)
static class Micrometer {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnSemanticConventions(SemanticConventions.OPEN_TELEMETRY)
static class Otel {
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2012-present 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
*
* https://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.micrometer.observation.autoconfigure.condition;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SemanticConventions}.
*
* @author Andy Wilkinson
*/
class SemanticConventionsTests {
@Test
void exactMatch() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("management.observations.conventions=MICROMETER").applyTo(environment);
assertThat(SemanticConventions.MICROMETER.isActive(environment)).isTrue();
assertThat(SemanticConventions.OPEN_TELEMETRY.isActive(environment)).isFalse();
}
@Test
void caseInsensitiveMatch() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("management.observations.conventions=open_telemetry").applyTo(environment);
assertThat(SemanticConventions.MICROMETER.isActive(environment)).isFalse();
assertThat(SemanticConventions.OPEN_TELEMETRY.isActive(environment)).isTrue();
}
@Test
void ignoresUnderscoreMatch() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("management.observations.conventions=opentelemetry").applyTo(environment);
assertThat(SemanticConventions.MICROMETER.isActive(environment)).isFalse();
assertThat(SemanticConventions.OPEN_TELEMETRY.isActive(environment)).isTrue();
}
}
@@ -25,13 +25,14 @@ 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.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.micrometer.metrics.MaximumAllowableTagsMeterFilter;
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.ConditionalOnSemanticConventions;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.SemanticConventions;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
@@ -71,20 +72,23 @@ public final class WebFluxObservationAutoConfiguration {
return new MaximumAllowableTagsMeterFilter(meterNamePrefix, "uri", maxUriTags);
}
@Bean
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "micrometer",
matchIfMissing = true)
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention() {
return new DefaultServerRequestObservationConvention(
this.observationProperties.getHttp().getServer().getRequests().getName());
}
static class ServerRequestObservationConventionConfiguration {
@Bean
@ConditionalOnSemanticConventions(SemanticConventions.MICROMETER)
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention(
ObservationProperties observationProperties) {
return new DefaultServerRequestObservationConvention(
observationProperties.getHttp().getServer().getRequests().getName());
}
@Bean
@ConditionalOnSemanticConventions(SemanticConventions.OPEN_TELEMETRY)
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
return new OpenTelemetryServerRequestObservationConvention();
}
@Bean
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "opentelemetry")
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
return new OpenTelemetryServerRequestObservationConvention();
}
}
@@ -27,13 +27,14 @@ 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.ConditionalOnMissingFilterBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.micrometer.metrics.MaximumAllowableTagsMeterFilter;
import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.ObservationProperties;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.ConditionalOnSemanticConventions;
import org.springframework.boot.micrometer.observation.autoconfigure.condition.SemanticConventions;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -65,23 +66,6 @@ import org.springframework.web.servlet.DispatcherServlet;
@EnableConfigurationProperties(ObservationProperties.class)
public final class WebMvcObservationAutoConfiguration {
@Bean
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "micrometer",
matchIfMissing = true)
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention(
ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName();
return new DefaultServerRequestObservationConvention(name);
}
@Bean
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
@ConditionalOnProperty(name = "management.observations.conventions", havingValue = "opentelemetry")
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
return new OpenTelemetryServerRequestObservationConvention();
}
@Bean
@ConditionalOnMissingFilterBean
FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(ObservationRegistry registry,
@@ -93,6 +77,25 @@ public final class WebMvcObservationAutoConfiguration {
return registration;
}
@ConditionalOnMissingBean(ServerRequestObservationConvention.class)
static class ServerRequestObservationConventionConfiguration {
@Bean
@ConditionalOnSemanticConventions(SemanticConventions.MICROMETER)
DefaultServerRequestObservationConvention micrometerServerRequestObservationConvention(
ObservationProperties observationProperties) {
String name = observationProperties.getHttp().getServer().getRequests().getName();
return new DefaultServerRequestObservationConvention(name);
}
@Bean
@ConditionalOnSemanticConventions(SemanticConventions.OPEN_TELEMETRY)
OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
return new OpenTelemetryServerRequestObservationConvention();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ MeterRegistry.class, MetricsProperties.class })
@ConditionalOnBean(MeterRegistry.class)