From 9bbfe7fe4e3f65cb698d6d2320ac87372d5d572f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Wed, 10 Jul 2024 19:32:17 +0200 Subject: [PATCH] Use config properties for spring starter (http server) (#11667) Co-authored-by: Lauri Tulmin Co-authored-by: Trask Stalnaker --- .../DefaultHttpServerInstrumenterBuilder.java | 219 ++++++++++++++++++ .../web/RestClientBeanPostProcessor.java | 2 +- .../jdbc/DataSourcePostProcessor.java | 2 +- ...lientInstrumentationAutoConfiguration.java | 2 +- .../R2dbcInstrumentingPostProcessor.java | 2 +- .../web/RestTemplateInstrumentation.java | 2 +- ...bfluxInstrumentationAutoConfiguration.java | 12 +- .../webflux/WebClientBeanPostProcessor.java | 22 +- ...bMvc5InstrumentationAutoConfiguration.java | 12 +- ...bMvc6InstrumentationAutoConfiguration.java | 12 +- .../internal/InstrumentationConfigUtil.java | 21 -- .../properties/InstrumentationConfigUtil.java | 40 +++- ...itional-spring-configuration-metadata.json | 16 ++ ...xInstrumentationAutoConfigurationTest.java | 6 + .../WebClientBeanPostProcessorTest.java | 25 +- ...Instrumentation5AutoConfigurationTest.java | 6 + ...Instrumentation6AutoConfigurationTest.java | 6 + .../webflux/v5_0/client/WebClientHelper.java | 23 +- .../v5_3/SpringWebfluxTelemetryBuilder.java | 136 ++++------- .../internal/ClientInstrumenterFactory.java | 80 ------- .../internal/SpringWebfluxBuilderUtil.java | 64 +++++ .../v5_3/SpringWebMvcTelemetryBuilder.java | 79 ++----- .../v5_3/internal/SpringMvcBuilderUtil.java | 41 ++++ .../v6_0/SpringWebMvcTelemetryBuilder.java | 78 ++----- .../v6_0/internal/SpringMvcBuilderUtil.java | 41 ++++ .../smoketest/OtelSpringStarterSmokeTest.java | 2 +- .../AbstractOtelSpringStarterSmokeTest.java | 18 +- .../src/main/resources/application.yaml | 2 + ...actOtelReactiveSpringStarterSmokeTest.java | 15 +- .../src/main/resources/application.yaml | 5 + .../spring/smoketest/HttpSpanDataAssert.java | 62 +++++ 31 files changed, 671 insertions(+), 382 deletions(-) create mode 100644 instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java delete mode 100644 instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/InstrumentationConfigUtil.java delete mode 100644 instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/ClientInstrumenterFactory.java create mode 100644 instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxBuilderUtil.java create mode 100644 instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/internal/SpringMvcBuilderUtil.java create mode 100644 instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/internal/SpringMvcBuilderUtil.java create mode 100644 smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/HttpSpanDataAssert.java diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java new file mode 100644 index 000000000000..b229a7126e48 --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/builder/internal/DefaultHttpServerInstrumenterBuilder.java @@ -0,0 +1,219 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.builder.internal; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; +import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; +import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder; +import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; +import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; +import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class DefaultHttpServerInstrumenterBuilder { + + private final String instrumentationName; + private final OpenTelemetry openTelemetry; + + private final List> additionalExtractors = + new ArrayList<>(); + private Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractorTransformer = Function.identity(); + private final HttpServerAttributesExtractorBuilder + httpAttributesExtractorBuilder; + private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder; + + @Nullable private TextMapGetter headerGetter; + private Function, ? extends SpanNameExtractor> + spanNameExtractorTransformer = Function.identity(); + private final HttpServerRouteBuilder httpServerRouteBuilder; + private final HttpServerAttributesGetter attributesGetter; + private boolean emitExperimentalHttpServerMetrics = false; + + public DefaultHttpServerInstrumenterBuilder( + String instrumentationName, + OpenTelemetry openTelemetry, + HttpServerAttributesGetter attributesGetter) { + this.instrumentationName = instrumentationName; + this.openTelemetry = openTelemetry; + httpAttributesExtractorBuilder = HttpServerAttributesExtractor.builder(attributesGetter); + httpSpanNameExtractorBuilder = HttpSpanNameExtractor.builder(attributesGetter); + httpServerRouteBuilder = HttpServerRoute.builder(attributesGetter); + this.attributesGetter = attributesGetter; + } + + /** + * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented + * items. + */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder addAttributesExtractor( + AttributesExtractor attributesExtractor) { + additionalExtractors.add(attributesExtractor); + return this; + } + + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setStatusExtractor( + Function< + SpanStatusExtractor, + ? extends SpanStatusExtractor> + statusExtractor) { + this.statusExtractorTransformer = statusExtractor; + return this; + } + + /** + * Configures the HTTP request headers that will be captured as span attributes. + * + * @param requestHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setCapturedRequestHeaders( + List requestHeaders) { + httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + return this; + } + + /** + * Configures the HTTP response headers that will be captured as span attributes. + * + * @param responseHeaders A list of HTTP header names. + */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setCapturedResponseHeaders( + List responseHeaders) { + httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + return this; + } + + /** + * Configures the instrumentation to recognize an alternative set of HTTP request methods. + * + *

By default, this instrumentation defines "known" methods as the ones listed in RFC9110 and the PATCH + * method defined in RFC5789. + * + *

Note: calling this method overrides the default known method sets completely; it does + * not supplement it. + * + * @param knownMethods A set of recognized HTTP request methods. + * @see HttpServerAttributesExtractorBuilder#setKnownMethods(Set) + */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setKnownMethods( + Set knownMethods) { + httpAttributesExtractorBuilder.setKnownMethods(knownMethods); + httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); + httpServerRouteBuilder.setKnownMethods(knownMethods); + return this; + } + + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setHeaderGetter( + @Nullable TextMapGetter headerGetter) { + this.headerGetter = headerGetter; + return this; + } + + /** + * Configures the instrumentation to emit experimental HTTP server metrics. + * + * @param emitExperimentalHttpServerMetrics {@code true} if the experimental HTTP server metrics + * are to be emitted. + */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder + setEmitExperimentalHttpServerMetrics(boolean emitExperimentalHttpServerMetrics) { + this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics; + return this; + } + + /** Sets custom {@link SpanNameExtractor} via transform function. */ + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder setSpanNameExtractor( + Function, ? extends SpanNameExtractor> + spanNameExtractorTransformer) { + this.spanNameExtractorTransformer = spanNameExtractorTransformer; + return this; + } + + public Instrumenter build() { + InstrumenterBuilder builder = builder(); + + if (headerGetter != null) { + return builder.buildServerInstrumenter(headerGetter); + } + return builder.buildInstrumenter(SpanKindExtractor.alwaysServer()); + } + + private InstrumenterBuilder builder() { + SpanNameExtractor spanNameExtractor = + spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build()); + + InstrumenterBuilder builder = + Instrumenter.builder( + openTelemetry, instrumentationName, spanNameExtractor) + .setSpanStatusExtractor( + statusExtractorTransformer.apply(HttpSpanStatusExtractor.create(attributesGetter))) + .addAttributesExtractor(httpAttributesExtractorBuilder.build()) + .addAttributesExtractors(additionalExtractors) + .addContextCustomizer(httpServerRouteBuilder.build()) + .addOperationMetrics(HttpServerMetrics.get()); + if (emitExperimentalHttpServerMetrics) { + builder + .addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter)) + .addOperationMetrics(HttpServerExperimentalMetrics.get()); + } + + return builder; + } + + @CanIgnoreReturnValue + public DefaultHttpServerInstrumenterBuilder configure(CommonConfig config) { + set(config::getKnownHttpRequestMethods, this::setKnownMethods); + set(config::getServerRequestHeaders, this::setCapturedRequestHeaders); + set(config::getServerResponseHeaders, this::setCapturedResponseHeaders); + set( + config::shouldEmitExperimentalHttpServerTelemetry, + this::setEmitExperimentalHttpServerMetrics); + return this; + } + + private static void set(Supplier supplier, Consumer consumer) { + T t = supplier.get(); + if (t != null) { + consumer.accept(t); + } + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure-3/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure-3/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestClientBeanPostProcessor.java index 469fdfadd741..afc7df6824b6 100644 --- a/instrumentation/spring/spring-boot-autoconfigure-3/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure-3/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestClientBeanPostProcessor.java @@ -56,7 +56,7 @@ private static RestClient addRestClientInterceptorIfNotPresent( static ClientHttpRequestInterceptor getInterceptor( OpenTelemetry openTelemetry, ConfigProperties config) { - return InstrumentationConfigUtil.configureBuilder( + return InstrumentationConfigUtil.configureClientBuilder( config, SpringWebTelemetry.builder(openTelemetry), WebTelemetryUtil.getBuilderExtractor()) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/jdbc/DataSourcePostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/jdbc/DataSourcePostProcessor.java index d733caa22b43..f0884f171e1f 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/jdbc/DataSourcePostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/jdbc/DataSourcePostProcessor.java @@ -8,7 +8,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.InstrumentationConfigUtil; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import javax.sql.DataSource; import org.springframework.aop.scope.ScopedProxyUtils; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java index 0eee74e37965..7885c2088aea 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/mongo/MongoClientInstrumentationAutoConfiguration.java @@ -9,7 +9,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.InstrumentationConfigUtil; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java index d3bddb3d8055..95ad7aad333c 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/r2dbc/R2dbcInstrumentingPostProcessor.java @@ -7,7 +7,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.r2dbc.v1_0.R2dbcTelemetry; -import io.opentelemetry.instrumentation.spring.autoconfigure.internal.InstrumentationConfigUtil; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryOptions; diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestTemplateInstrumentation.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestTemplateInstrumentation.java index deb84cfcc23b..6cd79283afae 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestTemplateInstrumentation.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/web/RestTemplateInstrumentation.java @@ -24,7 +24,7 @@ static RestTemplate addIfNotPresent( RestTemplate restTemplate, OpenTelemetry openTelemetry, ConfigProperties config) { ClientHttpRequestInterceptor instrumentationInterceptor = - InstrumentationConfigUtil.configureBuilder( + InstrumentationConfigUtil.configureClientBuilder( config, SpringWebTelemetry.builder(openTelemetry), WebTelemetryUtil.getBuilderExtractor()) diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java index cfd5f5eb5ed7..7a0cc4f29d91 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java @@ -7,7 +7,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -30,14 +30,14 @@ public SpringWebfluxInstrumentationAutoConfiguration() {} // static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning @Bean static WebClientBeanPostProcessor otelWebClientBeanPostProcessor( - ObjectProvider openTelemetryProvider) { - return new WebClientBeanPostProcessor(openTelemetryProvider); + ObjectProvider openTelemetryProvider, + ObjectProvider configPropertiesProvider) { + return new WebClientBeanPostProcessor(openTelemetryProvider, configPropertiesProvider); } @Bean - WebFilter telemetryFilter(OpenTelemetry openTelemetry) { - return SpringWebfluxTelemetry.builder(openTelemetry) - .build() + WebFilter telemetryFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + return WebClientBeanPostProcessor.getWebfluxTelemetry(openTelemetry, config) .createWebFilterAndRegisterReactorHook(); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessor.java index 1ee2c56c1da0..d4a995da8297 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessor.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessor.java @@ -6,7 +6,10 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.webflux; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry; +import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxBuilderUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.web.reactive.function.client.WebClient; @@ -19,9 +22,23 @@ final class WebClientBeanPostProcessor implements BeanPostProcessor { private final ObjectProvider openTelemetryProvider; + private final ObjectProvider configPropertiesProvider; - WebClientBeanPostProcessor(ObjectProvider openTelemetryProvider) { + WebClientBeanPostProcessor( + ObjectProvider openTelemetryProvider, + ObjectProvider configPropertiesProvider) { this.openTelemetryProvider = openTelemetryProvider; + this.configPropertiesProvider = configPropertiesProvider; + } + + static SpringWebfluxTelemetry getWebfluxTelemetry( + OpenTelemetry openTelemetry, ConfigProperties config) { + return InstrumentationConfigUtil.configureClientAndServerBuilder( + config, + SpringWebfluxTelemetry.builder(openTelemetry), + SpringWebfluxBuilderUtil.getClientBuilderExtractor(), + SpringWebfluxBuilderUtil.getServerBuilderExtractor()) + .build(); } @Override @@ -38,7 +55,8 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { private WebClient.Builder wrapBuilder(WebClient.Builder webClientBuilder) { SpringWebfluxTelemetry instrumentation = - SpringWebfluxTelemetry.create(openTelemetryProvider.getObject()); + getWebfluxTelemetry( + openTelemetryProvider.getObject(), configPropertiesProvider.getObject()); return webClientBuilder.filters(instrumentation::addClientTracingFilter); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java index a57d2ab79df5..ccd077a42d09 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc5InstrumentationAutoConfiguration.java @@ -7,7 +7,10 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetry; +import io.opentelemetry.instrumentation.spring.webmvc.v5_3.internal.SpringMvcBuilderUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import javax.servlet.Filter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -22,7 +25,12 @@ public class SpringWebMvc5InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry) { - return SpringWebMvcTelemetry.create(openTelemetry).createServletFilter(); + Filter otelWebMvcFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + return InstrumentationConfigUtil.configureServerBuilder( + config, + SpringWebMvcTelemetry.builder(openTelemetry), + SpringMvcBuilderUtil.getBuilderExtractor()) + .build() + .createServletFilter(); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java index ca68d0e90d19..18dd08df56ef 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvc6InstrumentationAutoConfiguration.java @@ -7,7 +7,10 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; import io.opentelemetry.instrumentation.spring.webmvc.v6_0.SpringWebMvcTelemetry; +import io.opentelemetry.instrumentation.spring.webmvc.v6_0.internal.SpringMvcBuilderUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import jakarta.servlet.Filter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; @@ -22,7 +25,12 @@ public class SpringWebMvc6InstrumentationAutoConfiguration { @Bean - Filter otelWebMvcFilter(OpenTelemetry openTelemetry) { - return SpringWebMvcTelemetry.create(openTelemetry).createServletFilter(); + Filter otelWebMvcFilter(OpenTelemetry openTelemetry, ConfigProperties config) { + return InstrumentationConfigUtil.configureServerBuilder( + config, + SpringWebMvcTelemetry.builder(openTelemetry), + SpringMvcBuilderUtil.getBuilderExtractor()) + .build() + .createServletFilter(); } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/InstrumentationConfigUtil.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/InstrumentationConfigUtil.java deleted file mode 100644 index 0fec424a5e85..000000000000 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/InstrumentationConfigUtil.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.autoconfigure.internal; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -public class InstrumentationConfigUtil { - private InstrumentationConfigUtil() {} - - public static boolean isStatementSanitizationEnabled(ConfigProperties config, String key) { - return config.getBoolean( - key, config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true)); - } -} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java index 692f7296b375..747cf33dcb09 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java @@ -7,6 +7,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.function.Function; @@ -15,15 +16,48 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class InstrumentationConfigUtil { +public final class InstrumentationConfigUtil { private InstrumentationConfigUtil() {} @CanIgnoreReturnValue - public static T configureBuilder( + public static + T configureClientAndServerBuilder( + ConfigProperties config, + T builder, + Function> + getClientBuilder, + Function> + getServerBuilder) { + CommonConfig commonConfig = getConfig(config); + getClientBuilder.apply(builder).configure(commonConfig); + getServerBuilder.apply(builder).configure(commonConfig); + return builder; + } + + @CanIgnoreReturnValue + public static T configureClientBuilder( ConfigProperties config, T builder, Function> getBuilder) { - getBuilder.apply(builder).configure(new CommonConfig(new ConfigPropertiesBridge(config))); + getBuilder.apply(builder).configure(getConfig(config)); + return builder; + } + + @CanIgnoreReturnValue + public static T configureServerBuilder( + ConfigProperties config, + T builder, + Function> getBuilder) { + getBuilder.apply(builder).configure(getConfig(config)); return builder; } + + private static CommonConfig getConfig(ConfigProperties config) { + return new CommonConfig(new ConfigPropertiesBridge(config)); + } + + public static boolean isStatementSanitizationEnabled(ConfigProperties config, String key) { + return config.getBoolean( + key, config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true)); + } } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 19f162d812d0..3b309aae4739 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -312,6 +312,22 @@ "description": "Configures the instrumentation to recognize an alternative set of HTTP request methods. All other methods will be treated as _OTHER.", "defaultValue": "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE" }, + { + "name": "otel.instrumentation.http.server.capture-request-headers", + "type": "java.util.List", + "description": "List of HTTP request headers to capture in HTTP servers." + }, + { + "name": "otel.instrumentation.http.server.capture-response-headers", + "type": "java.util.List", + "description": "List of HTTP response headers to capture in HTTP servers." + }, + { + "name": "otel.instrumentation.http.server.emit-experimental-telemetry", + "type": "java.lang.Boolean", + "description": "Enable the capture of experimental HTTP server telemetry. Add the http.request.body.size and http.response.body.size attributes to spans, and record the http.server.request.body.size and http.server.response.body.size metrics.", + "defaultValue": false + }, { "name": "otel.instrumentation.jdbc.enabled", "type": "java.lang.Boolean", diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java index aeecebf37807..ba1bca851cf7 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfigurationTest.java @@ -8,6 +8,9 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.util.Collections; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -17,6 +20,9 @@ class SpringWebfluxInstrumentationAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) + .withBean( + ConfigProperties.class, + () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) .withConfiguration( AutoConfigurations.of(SpringWebfluxInstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessorTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessorTest.java index 55abca473a01..7289ea828950 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessorTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webflux/WebClientBeanPostProcessorTest.java @@ -8,6 +8,9 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.util.Collections; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -20,6 +23,8 @@ class WebClientBeanPostProcessorTest { static { beanFactory.registerSingleton("openTelemetry", OpenTelemetry.noop()); + beanFactory.registerSingleton( + "configProperties", DefaultConfigProperties.createFromMap(Collections.emptyMap())); } @Test @@ -27,7 +32,9 @@ class WebClientBeanPostProcessorTest { "when processed bean is NOT of type WebClient or WebClientBuilder should return Object") void returnsObject() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); + new WebClientBeanPostProcessor( + beanFactory.getBeanProvider(OpenTelemetry.class), + beanFactory.getBeanProvider(ConfigProperties.class)); assertThat(underTest.postProcessAfterInitialization(new Object(), "testObject")) .isExactlyInstanceOf(Object.class); @@ -37,7 +44,9 @@ void returnsObject() { @DisplayName("when processed bean is of type WebClient should return WebClient") void returnsWebClient() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); + new WebClientBeanPostProcessor( + beanFactory.getBeanProvider(OpenTelemetry.class), + beanFactory.getBeanProvider(ConfigProperties.class)); assertThat(underTest.postProcessAfterInitialization(WebClient.create(), "testWebClient")) .isInstanceOf(WebClient.class); @@ -47,7 +56,9 @@ void returnsWebClient() { @DisplayName("when processed bean is of type WebClientBuilder should return WebClientBuilder") void returnsWebClientBuilder() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); + new WebClientBeanPostProcessor( + beanFactory.getBeanProvider(OpenTelemetry.class), + beanFactory.getBeanProvider(ConfigProperties.class)); assertThat( underTest.postProcessAfterInitialization(WebClient.builder(), "testWebClientBuilder")) @@ -58,7 +69,9 @@ void returnsWebClientBuilder() { @DisplayName("when processed bean is of type WebClient should add exchange filter to WebClient") void addsExchangeFilterWebClient() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); + new WebClientBeanPostProcessor( + beanFactory.getBeanProvider(OpenTelemetry.class), + beanFactory.getBeanProvider(ConfigProperties.class)); WebClient webClient = WebClient.create(); Object processedWebClient = @@ -81,7 +94,9 @@ void addsExchangeFilterWebClient() { "when processed bean is of type WebClientBuilder should add ONE exchange filter to WebClientBuilder") void addsExchangeFilterWebClientBuilder() { BeanPostProcessor underTest = - new WebClientBeanPostProcessor(beanFactory.getBeanProvider(OpenTelemetry.class)); + new WebClientBeanPostProcessor( + beanFactory.getBeanProvider(OpenTelemetry.class), + beanFactory.getBeanProvider(ConfigProperties.class)); WebClient.Builder webClientBuilder = WebClient.builder(); underTest.postProcessAfterInitialization(webClientBuilder, "testWebClientBuilder"); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java index fffc164eabfd..fd3144b46e5b 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation5AutoConfigurationTest.java @@ -9,6 +9,9 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.util.Collections; import javax.servlet.Filter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,6 +23,9 @@ class SpringWebMvcInstrumentation5AutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) + .withBean( + ConfigProperties.class, + () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) .withConfiguration( AutoConfigurations.of(SpringWebMvc5InstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java index 4b37d5819b10..d84df1a203ef 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/instrumentation/webmvc/SpringWebMvcInstrumentation6AutoConfigurationTest.java @@ -9,7 +9,10 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import jakarta.servlet.Filter; +import java.util.Collections; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -20,6 +23,9 @@ class SpringWebMvcInstrumentation6AutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(OpenTelemetry.class, OpenTelemetry::noop) + .withBean( + ConfigProperties.class, + () -> DefaultConfigProperties.createFromMap(Collections.emptyMap())) .withConfiguration( AutoConfigurations.of(SpringWebMvc6InstrumentationAutoConfiguration.class)); diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java index b13e8a67a2e8..99ee1a3f9273 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java @@ -5,17 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.client; -import static java.util.Collections.singletonList; - import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientPeerServiceAttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.ClientInstrumenterFactory; import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientHttpAttributesGetter; import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientTracingFilter; -import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpClientInstrumenters; import java.util.List; -import java.util.function.Function; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @@ -23,20 +18,8 @@ public final class WebClientHelper { private static final Instrumenter instrumenter = - ClientInstrumenterFactory.create( - GlobalOpenTelemetry.get(), - builder -> - builder - .setCapturedRequestHeaders(AgentCommonConfig.get().getClientRequestHeaders()) - .setCapturedResponseHeaders(AgentCommonConfig.get().getClientResponseHeaders()) - .setKnownMethods(AgentCommonConfig.get().getKnownHttpRequestMethods()), - builder -> builder.setKnownMethods(AgentCommonConfig.get().getKnownHttpRequestMethods()), - Function.identity(), - singletonList( - HttpClientPeerServiceAttributesExtractor.create( - WebClientHttpAttributesGetter.INSTANCE, - AgentCommonConfig.get().getPeerServiceResolver())), - AgentCommonConfig.get().shouldEmitExperimentalHttpClientTelemetry()); + JavaagentHttpClientInstrumenters.create( + "io.opentelemetry.spring-webflux-5.0", WebClientHttpAttributesGetter.INSTANCE); public static void addFilter(List exchangeFilterFunctions) { for (ExchangeFilterFunction filterFunction : exchangeFilterFunctions) { diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java index a24930d01695..e8387880cd32 100644 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java @@ -7,26 +7,16 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; -import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.ClientInstrumenterFactory; -import java.util.ArrayList; +import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxBuilderUtil; +import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientHttpAttributesGetter; import java.util.List; import java.util.Set; -import java.util.function.Consumer; import java.util.function.Function; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; @@ -36,38 +26,25 @@ public final class SpringWebfluxTelemetryBuilder { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webflux-5.3"; - private final OpenTelemetry openTelemetry; + private final DefaultHttpClientInstrumenterBuilder clientBuilder; + private final DefaultHttpServerInstrumenterBuilder + serverBuilder; - private final List> - clientAdditionalExtractors = new ArrayList<>(); - private final List> - serverAdditionalExtractors = new ArrayList<>(); - - private final HttpServerAttributesExtractorBuilder - httpServerAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(WebfluxServerHttpAttributesGetter.INSTANCE); - private final HttpSpanNameExtractorBuilder httpServerSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(WebfluxServerHttpAttributesGetter.INSTANCE); - private final HttpServerRouteBuilder httpServerRouteBuilder = - HttpServerRoute.builder(WebfluxServerHttpAttributesGetter.INSTANCE); - - private Function< - SpanNameExtractor, ? extends SpanNameExtractor> - clientSpanNameExtractorTransformer = Function.identity(); - private Function< - SpanNameExtractor, - ? extends SpanNameExtractor> - serverSpanNameExtractorTransformer = Function.identity(); - - private Consumer> - clientExtractorConfigurer = builder -> {}; - private Consumer> clientSpanNameExtractorConfigurer = - builder -> {}; - private boolean emitExperimentalHttpClientTelemetry = false; - private boolean emitExperimentalHttpServerTelemetry = false; + static { + SpringWebfluxBuilderUtil.setClientBuilderExtractor( + SpringWebfluxTelemetryBuilder::getClientBuilder); + SpringWebfluxBuilderUtil.setServerBuilderExtractor( + SpringWebfluxTelemetryBuilder::getServerBuilder); + } SpringWebfluxTelemetryBuilder(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + clientBuilder = + new DefaultHttpClientInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, WebClientHttpAttributesGetter.INSTANCE); + serverBuilder = + new DefaultHttpServerInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, WebfluxServerHttpAttributesGetter.INSTANCE) + .setHeaderGetter(WebfluxTextMapGetter.INSTANCE); } /** @@ -77,7 +54,7 @@ public final class SpringWebfluxTelemetryBuilder { @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder addClientAttributesExtractor( AttributesExtractor attributesExtractor) { - clientAdditionalExtractors.add(attributesExtractor); + clientBuilder.addAttributeExtractor(attributesExtractor); return this; } @@ -89,9 +66,7 @@ public SpringWebfluxTelemetryBuilder addClientAttributesExtractor( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedClientRequestHeaders( List requestHeaders) { - clientExtractorConfigurer = - clientExtractorConfigurer.andThen( - builder -> builder.setCapturedRequestHeaders(requestHeaders)); + clientBuilder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -103,9 +78,7 @@ public SpringWebfluxTelemetryBuilder setCapturedClientRequestHeaders( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedClientResponseHeaders( List responseHeaders) { - clientExtractorConfigurer = - clientExtractorConfigurer.andThen( - builder -> builder.setCapturedResponseHeaders(responseHeaders)); + clientBuilder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -116,7 +89,7 @@ public SpringWebfluxTelemetryBuilder setCapturedClientResponseHeaders( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder addServerAttributesExtractor( AttributesExtractor attributesExtractor) { - serverAdditionalExtractors.add(attributesExtractor); + serverBuilder.addAttributesExtractor(attributesExtractor); return this; } @@ -129,7 +102,7 @@ public SpringWebfluxTelemetryBuilder addServerAttributesExtractor( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedServerRequestHeaders( List requestHeaders) { - httpServerAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + serverBuilder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -142,7 +115,7 @@ public SpringWebfluxTelemetryBuilder setCapturedServerRequestHeaders( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setCapturedServerResponseHeaders( List responseHeaders) { - httpServerAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + serverBuilder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -162,13 +135,8 @@ public SpringWebfluxTelemetryBuilder setCapturedServerResponseHeaders( */ @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setKnownMethods(Set knownMethods) { - clientExtractorConfigurer = - clientExtractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); - clientSpanNameExtractorConfigurer = - clientSpanNameExtractorConfigurer.andThen(builder -> builder.setKnownMethods(knownMethods)); - httpServerAttributesExtractorBuilder.setKnownMethods(knownMethods); - httpServerSpanNameExtractorBuilder.setKnownMethods(knownMethods); - httpServerRouteBuilder.setKnownMethods(knownMethods); + clientBuilder.setKnownMethods(knownMethods); + serverBuilder.setKnownMethods(knownMethods); return this; } @@ -181,7 +149,7 @@ public SpringWebfluxTelemetryBuilder setKnownMethods(Set knownMethods) { @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setEmitExperimentalHttpClientTelemetry( boolean emitExperimentalHttpClientTelemetry) { - this.emitExperimentalHttpClientTelemetry = emitExperimentalHttpClientTelemetry; + clientBuilder.setEmitExperimentalHttpClientMetrics(emitExperimentalHttpClientTelemetry); return this; } @@ -194,7 +162,7 @@ public SpringWebfluxTelemetryBuilder setEmitExperimentalHttpClientTelemetry( @CanIgnoreReturnValue public SpringWebfluxTelemetryBuilder setEmitExperimentalHttpServerTelemetry( boolean emitExperimentalHttpServerTelemetry) { - this.emitExperimentalHttpServerTelemetry = emitExperimentalHttpServerTelemetry; + serverBuilder.setEmitExperimentalHttpServerMetrics(emitExperimentalHttpServerTelemetry); return this; } @@ -203,7 +171,7 @@ public SpringWebfluxTelemetryBuilder setEmitExperimentalHttpServerTelemetry( public SpringWebfluxTelemetryBuilder setClientSpanNameExtractor( Function, ? extends SpanNameExtractor> clientSpanNameExtractor) { - this.clientSpanNameExtractorTransformer = clientSpanNameExtractor; + clientBuilder.setSpanNameExtractor(clientSpanNameExtractor); return this; } @@ -214,7 +182,7 @@ public SpringWebfluxTelemetryBuilder setServerSpanNameExtractor( SpanNameExtractor, ? extends SpanNameExtractor> serverSpanNameExtractor) { - this.serverSpanNameExtractorTransformer = serverSpanNameExtractor; + serverBuilder.setSpanNameExtractor(serverSpanNameExtractor); return this; } @@ -223,40 +191,18 @@ public SpringWebfluxTelemetryBuilder setServerSpanNameExtractor( * SpringWebfluxTelemetryBuilder}. */ public SpringWebfluxTelemetry build() { - Instrumenter clientInstrumenter = - ClientInstrumenterFactory.create( - openTelemetry, - clientExtractorConfigurer, - clientSpanNameExtractorConfigurer, - clientSpanNameExtractorTransformer, - clientAdditionalExtractors, - emitExperimentalHttpClientTelemetry); - - Instrumenter serverInstrumenter = - buildServerInstrumenter(); - return new SpringWebfluxTelemetry( - clientInstrumenter, serverInstrumenter, openTelemetry.getPropagators()); + clientBuilder.build(), + serverBuilder.build(), + clientBuilder.getOpenTelemetry().getPropagators()); } - private Instrumenter buildServerInstrumenter() { - WebfluxServerHttpAttributesGetter getter = WebfluxServerHttpAttributesGetter.INSTANCE; - SpanNameExtractor spanNameExtractor = - serverSpanNameExtractorTransformer.apply(httpServerSpanNameExtractorBuilder.build()); + private DefaultHttpClientInstrumenterBuilder getClientBuilder() { + return clientBuilder; + } - InstrumenterBuilder builder = - Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .setSpanStatusExtractor(HttpSpanStatusExtractor.create(getter)) - .addAttributesExtractor(httpServerAttributesExtractorBuilder.build()) - .addAttributesExtractors(serverAdditionalExtractors) - .addContextCustomizer(httpServerRouteBuilder.build()) - .addOperationMetrics(HttpServerMetrics.get()); - if (emitExperimentalHttpServerTelemetry) { - builder - .addAttributesExtractor(HttpExperimentalAttributesExtractor.create(getter)) - .addOperationMetrics(HttpServerExperimentalMetrics.get()); - } - return builder.buildServerInstrumenter(WebfluxTextMapGetter.INSTANCE); + private DefaultHttpServerInstrumenterBuilder + getServerBuilder() { + return serverBuilder; } } diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/ClientInstrumenterFactory.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/ClientInstrumenterFactory.java deleted file mode 100644 index bb8217539da3..000000000000 --- a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/ClientInstrumenterFactory.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal; - -import static io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor.alwaysClient; - -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpClientExperimentalMetrics; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; -import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpClientMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; - -/** - * This class is internal and is hence not for public use. Its APIs are unstable and can change at - * any time. - */ -// client builder is separate so that it can be used by javaagent instrumentation -// which supports 5.0, without triggering the server instrumentation which depends on webflux 5.3 -public final class ClientInstrumenterFactory { - - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webflux-5.3"; - - public static Instrumenter create( - OpenTelemetry openTelemetry, - Consumer> - extractorConfigurer, - Consumer> spanNameExtractorConfigurer, - Function, ? extends SpanNameExtractor> - spanNameExtractorTransformer, - List> additionalExtractors, - boolean emitExperimentalHttpClientTelemetry) { - - WebClientHttpAttributesGetter httpAttributesGetter = WebClientHttpAttributesGetter.INSTANCE; - - HttpClientAttributesExtractorBuilder extractorBuilder = - HttpClientAttributesExtractor.builder(httpAttributesGetter); - extractorConfigurer.accept(extractorBuilder); - - HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(httpAttributesGetter); - spanNameExtractorConfigurer.accept(httpSpanNameExtractorBuilder); - SpanNameExtractor spanNameExtractor = - spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build()); - - InstrumenterBuilder clientBuilder = - Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(extractorBuilder.build()) - .addAttributesExtractors(additionalExtractors) - .addOperationMetrics(HttpClientMetrics.get()); - - if (emitExperimentalHttpClientTelemetry) { - clientBuilder - .addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter)) - .addOperationMetrics(HttpClientExperimentalMetrics.get()); - } - - // headers are injected elsewhere; ClientRequest is immutable - return clientBuilder.buildInstrumenter(alwaysClient()); - } - - private ClientInstrumenterFactory() {} -} diff --git a/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxBuilderUtil.java b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxBuilderUtil.java new file mode 100644 index 000000000000..ec1d643b1d88 --- /dev/null +++ b/instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxBuilderUtil.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal; + +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; +import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetryBuilder; +import java.util.function.Function; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.server.ServerWebExchange; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class SpringWebfluxBuilderUtil { + private SpringWebfluxBuilderUtil() {} + + // allows access to the private field for the spring starter + private static Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + clientBuilderExtractor; + + // allows access to the private field for the spring starter + private static Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + serverBuilderExtractor; + + public static Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + getServerBuilderExtractor() { + return serverBuilderExtractor; + } + + public static void setServerBuilderExtractor( + Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + serverBuilderExtractor) { + SpringWebfluxBuilderUtil.serverBuilderExtractor = serverBuilderExtractor; + } + + public static Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + getClientBuilderExtractor() { + return clientBuilderExtractor; + } + + public static void setClientBuilderExtractor( + Function< + SpringWebfluxTelemetryBuilder, + DefaultHttpClientInstrumenterBuilder> + clientBuilderExtractor) { + SpringWebfluxBuilderUtil.clientBuilderExtractor = clientBuilderExtractor; + } +} diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java index 67a3a000b3ae..588263aff6e6 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/SpringWebMvcTelemetryBuilder.java @@ -7,21 +7,11 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; -import java.util.ArrayList; +import io.opentelemetry.instrumentation.spring.webmvc.v5_3.internal.SpringMvcBuilderUtil; import java.util.List; import java.util.Set; import java.util.function.Function; @@ -33,24 +23,18 @@ public final class SpringWebMvcTelemetryBuilder { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webmvc-5.3"; - private final OpenTelemetry openTelemetry; - private final List> - additionalExtractors = new ArrayList<>(); - private final HttpServerAttributesExtractorBuilder - httpAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private final HttpServerRouteBuilder httpServerRouteBuilder = - HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private Function< - SpanNameExtractor, - ? extends SpanNameExtractor> - spanNameExtractorTransformer = Function.identity(); - private boolean emitExperimentalHttpServerMetrics = false; + private final DefaultHttpServerInstrumenterBuilder + builder; + + static { + SpringMvcBuilderUtil.setBuilderExtractor(SpringWebMvcTelemetryBuilder::getBuilder); + } SpringWebMvcTelemetryBuilder(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + builder = + new DefaultHttpServerInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, SpringWebMvcHttpAttributesGetter.INSTANCE) + .setHeaderGetter(JavaxHttpServletRequestGetter.INSTANCE); } /** @@ -60,7 +44,7 @@ public final class SpringWebMvcTelemetryBuilder { @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { - additionalExtractors.add(attributesExtractor); + builder.addAttributesExtractor(attributesExtractor); return this; } @@ -71,7 +55,7 @@ public SpringWebMvcTelemetryBuilder addAttributesExtractor( */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { - httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + builder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -82,7 +66,7 @@ public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List reque */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { - httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + builder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -93,7 +77,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractor) { - this.spanNameExtractorTransformer = spanNameExtractor; + builder.setSpanNameExtractor(spanNameExtractor); return this; } @@ -112,9 +96,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { - httpAttributesExtractorBuilder.setKnownMethods(knownMethods); - httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); - httpServerRouteBuilder.setKnownMethods(knownMethods); + builder.setKnownMethods(knownMethods); return this; } @@ -127,7 +109,7 @@ public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setEmitExperimentalHttpServerMetrics( boolean emitExperimentalHttpServerMetrics) { - this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics; + builder.setEmitExperimentalHttpServerMetrics(emitExperimentalHttpServerMetrics); return this; } @@ -136,26 +118,11 @@ public SpringWebMvcTelemetryBuilder setEmitExperimentalHttpServerMetrics( * SpringWebMvcTelemetryBuilder}. */ public SpringWebMvcTelemetry build() { - SpringWebMvcHttpAttributesGetter httpAttributesGetter = - SpringWebMvcHttpAttributesGetter.INSTANCE; - SpanNameExtractor spanNameExtractor = - spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build()); - - InstrumenterBuilder builder = - Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(httpAttributesExtractorBuilder.build()) - .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(httpServerRouteBuilder.build()) - .addOperationMetrics(HttpServerMetrics.get()); - if (emitExperimentalHttpServerMetrics) { - builder - .addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter)) - .addOperationMetrics(HttpServerExperimentalMetrics.get()); - } + return new SpringWebMvcTelemetry(builder.build()); + } - return new SpringWebMvcTelemetry( - builder.buildServerInstrumenter(JavaxHttpServletRequestGetter.INSTANCE)); + public DefaultHttpServerInstrumenterBuilder + getBuilder() { + return builder; } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/internal/SpringMvcBuilderUtil.java b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/internal/SpringMvcBuilderUtil.java new file mode 100644 index 000000000000..74ac99e33d8a --- /dev/null +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v5_3/internal/SpringMvcBuilderUtil.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v5_3.internal; + +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; +import io.opentelemetry.instrumentation.spring.webmvc.v5_3.SpringWebMvcTelemetryBuilder; +import java.util.function.Function; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class SpringMvcBuilderUtil { + private SpringMvcBuilderUtil() {} + + // allows access to the private field for the spring starter + private static Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + builderExtractor; + + public static Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + getBuilderExtractor() { + return builderExtractor; + } + + public static void setBuilderExtractor( + Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + builderExtractor) { + SpringMvcBuilderUtil.builderExtractor = builderExtractor; + } +} diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java index 9b3e3b9e4e48..7978664dd053 100644 --- a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/SpringWebMvcTelemetryBuilder.java @@ -7,23 +7,13 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpExperimentalAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.http.HttpServerExperimentalMetrics; +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerMetrics; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute; -import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractor; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanNameExtractorBuilder; -import io.opentelemetry.instrumentation.api.semconv.http.HttpSpanStatusExtractor; +import io.opentelemetry.instrumentation.spring.webmvc.v6_0.internal.SpringMvcBuilderUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.function.Function; @@ -32,25 +22,18 @@ public final class SpringWebMvcTelemetryBuilder { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webmvc-6.0"; + private final DefaultHttpServerInstrumenterBuilder + builder; - private final OpenTelemetry openTelemetry; - private final List> - additionalExtractors = new ArrayList<>(); - private final HttpServerAttributesExtractorBuilder - httpAttributesExtractorBuilder = - HttpServerAttributesExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private final HttpSpanNameExtractorBuilder httpSpanNameExtractorBuilder = - HttpSpanNameExtractor.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private final HttpServerRouteBuilder httpServerRouteBuilder = - HttpServerRoute.builder(SpringWebMvcHttpAttributesGetter.INSTANCE); - private Function< - SpanNameExtractor, - ? extends SpanNameExtractor> - spanNameExtractorTransformer = Function.identity(); - private boolean emitExperimentalHttpServerMetrics = false; + static { + SpringMvcBuilderUtil.setBuilderExtractor(SpringWebMvcTelemetryBuilder::getBuilder); + } SpringWebMvcTelemetryBuilder(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + builder = + new DefaultHttpServerInstrumenterBuilder<>( + INSTRUMENTATION_NAME, openTelemetry, SpringWebMvcHttpAttributesGetter.INSTANCE) + .setHeaderGetter(JakartaHttpServletRequestGetter.INSTANCE); } /** @@ -60,7 +43,7 @@ public final class SpringWebMvcTelemetryBuilder { @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder addAttributesExtractor( AttributesExtractor attributesExtractor) { - additionalExtractors.add(attributesExtractor); + builder.addAttributesExtractor(attributesExtractor); return this; } @@ -71,7 +54,7 @@ public SpringWebMvcTelemetryBuilder addAttributesExtractor( */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List requestHeaders) { - httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders); + builder.setCapturedRequestHeaders(requestHeaders); return this; } @@ -82,7 +65,7 @@ public SpringWebMvcTelemetryBuilder setCapturedRequestHeaders(List reque */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setCapturedResponseHeaders(List responseHeaders) { - httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders); + builder.setCapturedResponseHeaders(responseHeaders); return this; } @@ -93,7 +76,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( SpanNameExtractor, ? extends SpanNameExtractor> spanNameExtractor) { - this.spanNameExtractorTransformer = spanNameExtractor; + builder.setSpanNameExtractor(spanNameExtractor); return this; } @@ -112,9 +95,7 @@ public SpringWebMvcTelemetryBuilder setSpanNameExtractor( */ @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { - httpAttributesExtractorBuilder.setKnownMethods(knownMethods); - httpSpanNameExtractorBuilder.setKnownMethods(knownMethods); - httpServerRouteBuilder.setKnownMethods(knownMethods); + builder.setKnownMethods(knownMethods); return this; } @@ -127,7 +108,7 @@ public SpringWebMvcTelemetryBuilder setKnownMethods(Set knownMethods) { @CanIgnoreReturnValue public SpringWebMvcTelemetryBuilder setEmitExperimentalHttpServerMetrics( boolean emitExperimentalHttpServerMetrics) { - this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics; + builder.setEmitExperimentalHttpServerMetrics(emitExperimentalHttpServerMetrics); return this; } @@ -136,26 +117,11 @@ public SpringWebMvcTelemetryBuilder setEmitExperimentalHttpServerMetrics( * SpringWebMvcTelemetryBuilder}. */ public SpringWebMvcTelemetry build() { - SpringWebMvcHttpAttributesGetter httpAttributesGetter = - SpringWebMvcHttpAttributesGetter.INSTANCE; - SpanNameExtractor spanNameExtractor = - spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build()); - - InstrumenterBuilder builder = - Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) - .addAttributesExtractor(httpAttributesExtractorBuilder.build()) - .addAttributesExtractors(additionalExtractors) - .addContextCustomizer(httpServerRouteBuilder.build()) - .addOperationMetrics(HttpServerMetrics.get()); - if (emitExperimentalHttpServerMetrics) { - builder - .addAttributesExtractor(HttpExperimentalAttributesExtractor.create(httpAttributesGetter)) - .addOperationMetrics(HttpServerExperimentalMetrics.get()); - } + return new SpringWebMvcTelemetry(builder.build()); + } - return new SpringWebMvcTelemetry( - builder.buildServerInstrumenter(JakartaHttpServletRequestGetter.INSTANCE)); + public DefaultHttpServerInstrumenterBuilder + getBuilder() { + return builder; } } diff --git a/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/internal/SpringMvcBuilderUtil.java b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/internal/SpringMvcBuilderUtil.java new file mode 100644 index 000000000000..d60102acd19a --- /dev/null +++ b/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/v6_0/internal/SpringMvcBuilderUtil.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.webmvc.v6_0.internal; + +import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; +import io.opentelemetry.instrumentation.spring.webmvc.v6_0.SpringWebMvcTelemetryBuilder; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.function.Function; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class SpringMvcBuilderUtil { + private SpringMvcBuilderUtil() {} + + // allows access to the private field for the spring starter + private static Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + builderExtractor; + + public static Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + getBuilderExtractor() { + return builderExtractor; + } + + public static void setBuilderExtractor( + Function< + SpringWebMvcTelemetryBuilder, + DefaultHttpServerInstrumenterBuilder> + builderExtractor) { + SpringMvcBuilderUtil.builderExtractor = builderExtractor; + } +} diff --git a/smoke-tests-otel-starter/spring-boot-3.2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-3.2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java index 84e97bdf84d5..f01bafb6e22e 100644 --- a/smoke-tests-otel-starter/spring-boot-3.2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-3.2/src/test/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java @@ -51,7 +51,7 @@ void restClient() { private static void assertClient(TraceAssert traceAssert) { traceAssert.hasSpansSatisfyingExactly( - span -> AbstractOtelSpringStarterSmokeTest.assertClientSpan(span, "/ping"), + span -> HttpSpanDataAssert.create(span).assertClientGetRequest("/ping"), span -> span.hasKind(SpanKind.SERVER).hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping"), span -> withSpanAssert(span)); } diff --git a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java index e3c48ca0d25c..4e802dd3bccb 100644 --- a/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelSpringStarterSmokeTest.java @@ -21,20 +21,17 @@ import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; import io.opentelemetry.semconv.ClientAttributes; import io.opentelemetry.semconv.HttpAttributes; import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.UrlAttributes; import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; -import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes; import io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes; import java.util.Collections; import java.util.List; import org.assertj.core.api.AbstractCharSequenceAssert; import org.assertj.core.api.AbstractIterableAssert; -import org.assertj.core.api.AbstractLongAssert; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -155,8 +152,8 @@ void shouldSendTelemetry() { ServerAttributes.SERVER_PORT, integerAssert -> integerAssert.isNotZero())), serverSpan -> - serverSpan - .hasKind(SpanKind.SERVER) + HttpSpanDataAssert.create(serverSpan) + .assertServerGetRequest("/ping") .hasResourceSatisfying( r -> r.hasAttribute( @@ -235,18 +232,9 @@ void restTemplate() { testing.waitAndAssertTraces( traceAssert -> traceAssert.hasSpansSatisfyingExactly( - span -> assertClientSpan(span, "/ping"), + span -> HttpSpanDataAssert.create(span).assertClientGetRequest("/ping"), span -> span.hasKind(SpanKind.SERVER).hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping"), span -> withSpanAssert(span))); } - - public static void assertClientSpan(SpanDataAssert span, String path) { - span.hasKind(SpanKind.CLIENT) - .hasAttributesSatisfying( - satisfies(UrlAttributes.URL_FULL, a -> a.endsWith(path)), - // this attribute is set by the experimental http instrumentation - satisfies( - HttpIncubatingAttributes.HTTP_RESPONSE_BODY_SIZE, AbstractLongAssert::isPositive)); - } } diff --git a/smoke-tests-otel-starter/spring-boot-common/src/main/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-common/src/main/resources/application.yaml index df0764948302..1995223b3841 100644 --- a/smoke-tests-otel-starter/spring-boot-common/src/main/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-common/src/main/resources/application.yaml @@ -11,6 +11,8 @@ otel: http: client: emit-experimental-telemetry: true + server: + emit-experimental-telemetry: true propagators: - b3 resource: diff --git a/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelReactiveSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelReactiveSpringStarterSmokeTest.java index 7bc32e819fcc..8de8f4fc0ac5 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelReactiveSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/java/io/opentelemetry/spring/smoketest/AbstractOtelReactiveSpringStarterSmokeTest.java @@ -8,8 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.semconv.HttpAttributes; -import io.opentelemetry.semconv.UrlAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,17 +50,8 @@ void webClientAndWebFluxAndR2dbc() { trace.hasSpansSatisfyingExactly(span -> span.hasName("CREATE TABLE testdb.player")), trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasKind(SpanKind.CLIENT) - .hasName("GET") - .hasAttributesSatisfying( - a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith("/webflux")), - span -> - span.hasKind(SpanKind.SERVER) - .hasName("GET /webflux") - .hasAttribute(HttpAttributes.HTTP_REQUEST_METHOD, "GET") - .hasAttribute(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200L) - .hasAttribute(HttpAttributes.HTTP_ROUTE, "/webflux"), + span -> HttpSpanDataAssert.create(span).assertClientGetRequest("/webflux"), + span -> HttpSpanDataAssert.create(span).assertServerGetRequest("/webflux"), span -> span.hasKind(SpanKind.CLIENT) .satisfies( diff --git a/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/resources/application.yaml index f0568098a28b..26b301f52d79 100644 --- a/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-reactive-common/src/main/resources/application.yaml @@ -3,6 +3,11 @@ otel: common: db-statement-sanitizer: enabled: false + http: + client: + emit-experimental-telemetry: true + server: + emit-experimental-telemetry: true spring: r2dbc: diff --git a/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/HttpSpanDataAssert.java b/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/HttpSpanDataAssert.java new file mode 100644 index 000000000000..4f6018d46167 --- /dev/null +++ b/smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/HttpSpanDataAssert.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.spring.smoketest; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.ResourceAssert; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes; +import java.util.function.Consumer; +import org.assertj.core.api.AbstractLongAssert; + +public final class HttpSpanDataAssert { + + private final SpanDataAssert span; + + private HttpSpanDataAssert(SpanDataAssert span) { + this.span = span; + } + + public static HttpSpanDataAssert create(SpanDataAssert serverSpan) { + return new HttpSpanDataAssert(serverSpan); + } + + @CanIgnoreReturnValue + public HttpSpanDataAssert assertClientGetRequest(String path) { + span.hasKind(SpanKind.CLIENT) + .hasAttributesSatisfying( + satisfies(UrlAttributes.URL_FULL, a -> a.endsWith(path)), + // this attribute is set by the experimental http instrumentation + satisfies( + HttpIncubatingAttributes.HTTP_RESPONSE_BODY_SIZE, + AbstractLongAssert::isNotNegative)); + return this; + } + + @CanIgnoreReturnValue + public HttpSpanDataAssert assertServerGetRequest(String route) { + span.hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"), + equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200L), + equalTo(HttpAttributes.HTTP_ROUTE, route), + // this attribute is set by the experimental http instrumentation + satisfies( + HttpIncubatingAttributes.HTTP_RESPONSE_BODY_SIZE, + AbstractLongAssert::isNotNegative)); + return this; + } + + public SpanDataAssert hasResourceSatisfying(Consumer resource) { + return span.hasResourceSatisfying(resource); + } +}