Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Use config properties for spring starter (http server) #11667

Merged
merged 18 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<REQUEST, RESPONSE> {

private final String instrumentationName;
private final OpenTelemetry openTelemetry;

private final List<AttributesExtractor<? super REQUEST, ? super RESPONSE>> additionalExtractors =
new ArrayList<>();
private Function<
SpanStatusExtractor<? super REQUEST, ? super RESPONSE>,
? extends SpanStatusExtractor<? super REQUEST, ? super RESPONSE>>
statusExtractorTransformer = Function.identity();
private final HttpServerAttributesExtractorBuilder<REQUEST, RESPONSE>
httpAttributesExtractorBuilder;
private final HttpSpanNameExtractorBuilder<REQUEST> httpSpanNameExtractorBuilder;

@Nullable private TextMapGetter<REQUEST> headerGetter;
private Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
spanNameExtractorTransformer = Function.identity();
private final HttpServerRouteBuilder<REQUEST> httpServerRouteBuilder;
private final HttpServerAttributesGetter<REQUEST, RESPONSE> attributesGetter;
private boolean emitExperimentalHttpServerMetrics = false;

public DefaultHttpServerInstrumenterBuilder(
String instrumentationName,
OpenTelemetry openTelemetry,
HttpServerAttributesGetter<REQUEST, RESPONSE> 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<REQUEST, RESPONSE> addAttributesExtractor(
AttributesExtractor<? super REQUEST, ? super RESPONSE> attributesExtractor) {
additionalExtractors.add(attributesExtractor);
return this;
}

@CanIgnoreReturnValue
public DefaultHttpServerInstrumenterBuilder<REQUEST, RESPONSE> setStatusExtractor(
Function<
SpanStatusExtractor<? super REQUEST, ? super RESPONSE>,
? extends SpanStatusExtractor<? super REQUEST, ? super RESPONSE>>
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<REQUEST, RESPONSE> setCapturedRequestHeaders(
List<String> 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<REQUEST, RESPONSE> setCapturedResponseHeaders(
List<String> responseHeaders) {
httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
return this;
}

/**
* Configures the instrumentation to recognize an alternative set of HTTP request methods.
*
* <p>By default, this instrumentation defines "known" methods as the ones listed in <a
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>.
*
* <p>Note: calling this method <b>overrides</b> 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<REQUEST, RESPONSE> setKnownMethods(
Set<String> knownMethods) {
httpAttributesExtractorBuilder.setKnownMethods(knownMethods);
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods);
httpServerRouteBuilder.setKnownMethods(knownMethods);
return this;
}

@CanIgnoreReturnValue
public DefaultHttpServerInstrumenterBuilder<REQUEST, RESPONSE> setHeaderGetter(
@Nullable TextMapGetter<REQUEST> 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<REQUEST, RESPONSE>
setEmitExperimentalHttpServerMetrics(boolean emitExperimentalHttpServerMetrics) {
this.emitExperimentalHttpServerMetrics = emitExperimentalHttpServerMetrics;
return this;
}

/** Sets custom {@link SpanNameExtractor} via transform function. */
@CanIgnoreReturnValue
public DefaultHttpServerInstrumenterBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
Function<SpanNameExtractor<REQUEST>, ? extends SpanNameExtractor<? super REQUEST>>
spanNameExtractorTransformer) {
this.spanNameExtractorTransformer = spanNameExtractorTransformer;
return this;
}

public Instrumenter<REQUEST, RESPONSE> build() {
InstrumenterBuilder<REQUEST, RESPONSE> builder = builder();

if (headerGetter != null) {
return builder.buildServerInstrumenter(headerGetter);
}
return builder.buildInstrumenter(SpanKindExtractor.alwaysServer());
}

private InstrumenterBuilder<REQUEST, RESPONSE> builder() {
SpanNameExtractor<? super REQUEST> spanNameExtractor =
spanNameExtractorTransformer.apply(httpSpanNameExtractorBuilder.build());

InstrumenterBuilder<REQUEST, RESPONSE> builder =
Instrumenter.<REQUEST, RESPONSE>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<REQUEST, RESPONSE> 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 <T> void set(Supplier<T> supplier, Consumer<T> consumer) {
T t = supplier.get();
if (t != null) {
consumer.accept(t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,14 +30,14 @@ public SpringWebfluxInstrumentationAutoConfiguration() {}
// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
@Bean
static WebClientBeanPostProcessor otelWebClientBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider) {
return new WebClientBeanPostProcessor(openTelemetryProvider);
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,9 +22,23 @@
final class WebClientBeanPostProcessor implements BeanPostProcessor {

private final ObjectProvider<OpenTelemetry> openTelemetryProvider;
private final ObjectProvider<ConfigProperties> configPropertiesProvider;

WebClientBeanPostProcessor(ObjectProvider<OpenTelemetry> openTelemetryProvider) {
WebClientBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> 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
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
}
Loading
Loading