Skip to content

Commit 39627d2

Browse files
authored
Split Webflux into client and server (#12852)
1 parent 0428a9c commit 39627d2

File tree

17 files changed

+764
-65
lines changed

17 files changed

+764
-65
lines changed

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/SpringWebfluxInstrumentationAutoConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static WebClientBeanPostProcessor otelWebClientBeanPostProcessor(
4040

4141
@Bean
4242
WebFilter telemetryFilter(OpenTelemetry openTelemetry, ConfigProperties config) {
43-
return WebClientBeanPostProcessor.getWebfluxTelemetry(openTelemetry, config)
43+
return WebClientBeanPostProcessor.getWebfluxServerTelemetry(openTelemetry, config)
4444
.createWebFilterAndRegisterReactorHook();
4545
}
4646
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/webflux/WebClientBeanPostProcessor.java

+17-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import io.opentelemetry.api.OpenTelemetry;
99
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil;
10-
import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
10+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxClientTelemetry;
11+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxServerTelemetry;
1112
import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxBuilderUtil;
1213
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1314
import org.springframework.beans.factory.ObjectProvider;
@@ -31,12 +32,20 @@ final class WebClientBeanPostProcessor implements BeanPostProcessor {
3132
this.configPropertiesProvider = configPropertiesProvider;
3233
}
3334

34-
static SpringWebfluxTelemetry getWebfluxTelemetry(
35+
static SpringWebfluxClientTelemetry getWebfluxClientTelemetry(
3536
OpenTelemetry openTelemetry, ConfigProperties config) {
36-
return InstrumentationConfigUtil.configureClientAndServerBuilder(
37+
return InstrumentationConfigUtil.configureClientBuilder(
3738
config,
38-
SpringWebfluxTelemetry.builder(openTelemetry),
39-
SpringWebfluxBuilderUtil.getClientBuilderExtractor(),
39+
SpringWebfluxClientTelemetry.builder(openTelemetry),
40+
SpringWebfluxBuilderUtil.getClientBuilderExtractor())
41+
.build();
42+
}
43+
44+
static SpringWebfluxServerTelemetry getWebfluxServerTelemetry(
45+
OpenTelemetry openTelemetry, ConfigProperties config) {
46+
return InstrumentationConfigUtil.configureServerBuilder(
47+
config,
48+
SpringWebfluxServerTelemetry.builder(openTelemetry),
4049
SpringWebfluxBuilderUtil.getServerBuilderExtractor())
4150
.build();
4251
}
@@ -54,9 +63,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
5463
}
5564

5665
private WebClient.Builder wrapBuilder(WebClient.Builder webClientBuilder) {
57-
SpringWebfluxTelemetry instrumentation =
58-
getWebfluxTelemetry(
66+
SpringWebfluxClientTelemetry instrumentation =
67+
getWebfluxClientTelemetry(
5968
openTelemetryProvider.getObject(), configPropertiesProvider.getObject());
60-
return webClientBuilder.filters(instrumentation::addClientTracingFilter);
69+
return webClientBuilder.filters(instrumentation::addFilter);
6170
}
6271
}

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/properties/InstrumentationConfigUtil.java

-15
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,6 @@
1919
public final class InstrumentationConfigUtil {
2020
private InstrumentationConfigUtil() {}
2121

22-
@CanIgnoreReturnValue
23-
public static <T, CLIENTREQUEST, CLIENTRESPONSE, SERVERREQUEST, SERVERRESPONSE>
24-
T configureClientAndServerBuilder(
25-
ConfigProperties config,
26-
T builder,
27-
Function<T, DefaultHttpClientInstrumenterBuilder<CLIENTREQUEST, CLIENTRESPONSE>>
28-
getClientBuilder,
29-
Function<T, DefaultHttpServerInstrumenterBuilder<SERVERREQUEST, SERVERRESPONSE>>
30-
getServerBuilder) {
31-
CommonConfig commonConfig = getConfig(config);
32-
getClientBuilder.apply(builder).configure(commonConfig);
33-
getServerBuilder.apply(builder).configure(commonConfig);
34-
return builder;
35-
}
36-
3722
@CanIgnoreReturnValue
3823
public static <T, REQUEST, RESPONSE> T configureClientBuilder(
3924
ConfigProperties config,

instrumentation/spring/spring-webflux/spring-webflux-5.3/library/README.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ For this instrumentation, the minimum supported version of Spring Webflux is 5.3
99
For Maven, add to your `pom.xml`:
1010

1111
```xml
12+
1213
<dependencies>
1314
<dependency>
1415
<groupId>io.opentelemetry.instrumentation</groupId>
@@ -59,23 +60,24 @@ import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemet
5960

6061
@Configuration
6162
public class WebClientConfig {
62-
private final SpringWebfluxTelemetry webfluxTelemetry;
63+
private final SpringWebfluxClientTelemetry webfluxClientTelemetry;
64+
private final SpringWebfluxServerTelemetry webfluxServerTelemetry;
6365

6466
public WebClientConfig(OpenTelemetry openTelemetry) {
65-
this.webfluxTelemetry = SpringWebfluxTelemetry.builder(openTelemetry).build();
67+
this.webfluxClientTelemetry = SpringWebfluxClientTelemetry.builder(openTelemetry).build();
6668
}
6769

6870
// Adds instrumentation to WebClients
6971
@Bean
7072
public WebClient.Builder webClient() {
7173
WebClient webClient = WebClient.create();
72-
return webClient.mutate().filters(webfluxTelemetry::addClientTracingFilter);
74+
return webClient.mutate().filters(webfluxClientTelemetry::addFilter);
7375
}
7476

7577
// Adds instrumentation to Webflux server
7678
@Bean
7779
public WebFilter webFilter() {
78-
return webfluxTelemetry.createWebFilterAndRegisterReactorHook();
80+
return webfluxServerTelemetry.createWebFilterAndRegisterReactorHook();
7981
}
8082
}
8183
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.webflux.v5_3;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.context.propagation.ContextPropagators;
10+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
11+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientTracingFilter;
12+
import java.util.List;
13+
import org.springframework.web.reactive.function.client.ClientRequest;
14+
import org.springframework.web.reactive.function.client.ClientResponse;
15+
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
16+
17+
/** Entrypoint for instrumenting Spring Webflux HTTP clients. */
18+
public final class SpringWebfluxClientTelemetry {
19+
20+
/**
21+
* Returns a new {@link SpringWebfluxClientTelemetry} configured with the given {@link
22+
* OpenTelemetry}.
23+
*/
24+
public static SpringWebfluxClientTelemetry create(OpenTelemetry openTelemetry) {
25+
return builder(openTelemetry).build();
26+
}
27+
28+
/**
29+
* Returns a new {@link SpringWebfluxClientTelemetryBuilder} configured with the given {@link
30+
* OpenTelemetry}.
31+
*/
32+
public static SpringWebfluxClientTelemetryBuilder builder(OpenTelemetry openTelemetry) {
33+
return new SpringWebfluxClientTelemetryBuilder(openTelemetry);
34+
}
35+
36+
private final Instrumenter<ClientRequest, ClientResponse> clientInstrumenter;
37+
private final ContextPropagators propagators;
38+
39+
SpringWebfluxClientTelemetry(
40+
Instrumenter<ClientRequest, ClientResponse> clientInstrumenter,
41+
ContextPropagators propagators) {
42+
this.clientInstrumenter = clientInstrumenter;
43+
this.propagators = propagators;
44+
}
45+
46+
public void addFilter(List<ExchangeFilterFunction> exchangeFilterFunctions) {
47+
for (ExchangeFilterFunction filterFunction : exchangeFilterFunctions) {
48+
if (filterFunction instanceof WebClientTracingFilter) {
49+
return;
50+
}
51+
}
52+
exchangeFilterFunctions.add(new WebClientTracingFilter(clientInstrumenter, propagators));
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.webflux.v5_3;
7+
8+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
9+
import io.opentelemetry.api.OpenTelemetry;
10+
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder;
11+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
12+
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
13+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
14+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.Experimental;
15+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxBuilderUtil;
16+
import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientHttpAttributesGetter;
17+
import java.util.List;
18+
import java.util.Set;
19+
import java.util.function.Function;
20+
import org.springframework.web.reactive.function.client.ClientRequest;
21+
import org.springframework.web.reactive.function.client.ClientResponse;
22+
23+
/** A builder of {@link SpringWebfluxClientTelemetry}. */
24+
public final class SpringWebfluxClientTelemetryBuilder {
25+
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webflux-5.3";
26+
27+
private final DefaultHttpClientInstrumenterBuilder<ClientRequest, ClientResponse> builder;
28+
private final OpenTelemetry openTelemetry;
29+
30+
static {
31+
SpringWebfluxBuilderUtil.setClientBuilderExtractor(builder -> builder.builder);
32+
Experimental.setSetEmitExperimentalClientTelemetry(
33+
(builder, emit) -> builder.builder.setEmitExperimentalHttpClientMetrics(emit));
34+
}
35+
36+
SpringWebfluxClientTelemetryBuilder(OpenTelemetry openTelemetry) {
37+
builder =
38+
DefaultHttpClientInstrumenterBuilder.create(
39+
INSTRUMENTATION_NAME, openTelemetry, WebClientHttpAttributesGetter.INSTANCE);
40+
this.openTelemetry = openTelemetry;
41+
}
42+
43+
/**
44+
* Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
45+
* items for WebClient.
46+
*/
47+
@CanIgnoreReturnValue
48+
public SpringWebfluxClientTelemetryBuilder addAttributesExtractor(
49+
AttributesExtractor<ClientRequest, ClientResponse> attributesExtractor) {
50+
builder.addAttributesExtractor(attributesExtractor);
51+
return this;
52+
}
53+
54+
/**
55+
* Configures the HTTP WebClient request headers that will be captured as span attributes.
56+
*
57+
* @param requestHeaders A list of HTTP header names.
58+
*/
59+
@CanIgnoreReturnValue
60+
public SpringWebfluxClientTelemetryBuilder setCapturedRequestHeaders(
61+
List<String> requestHeaders) {
62+
builder.setCapturedRequestHeaders(requestHeaders);
63+
return this;
64+
}
65+
66+
/**
67+
* Configures the HTTP WebClient response headers that will be captured as span attributes.
68+
*
69+
* @param responseHeaders A list of HTTP header names.
70+
*/
71+
@CanIgnoreReturnValue
72+
public SpringWebfluxClientTelemetryBuilder setCapturedResponseHeaders(
73+
List<String> responseHeaders) {
74+
builder.setCapturedResponseHeaders(responseHeaders);
75+
return this;
76+
}
77+
78+
/**
79+
* Configures the instrumentation to recognize an alternative set of HTTP request methods.
80+
*
81+
* <p>By default, this instrumentation defines "known" methods as the ones listed in <a
82+
* href="https://www.rfc-editor.org/rfc/rfc9110.html#name-methods">RFC9110</a> and the PATCH
83+
* method defined in <a href="https://www.rfc-editor.org/rfc/rfc5789.html">RFC5789</a>.
84+
*
85+
* <p>Note: calling this method <b>overrides</b> the default known method sets completely; it does
86+
* not supplement it.
87+
*
88+
* @param knownMethods A set of recognized HTTP request methods.
89+
* @see HttpClientAttributesExtractorBuilder#setKnownMethods(Set)
90+
*/
91+
@CanIgnoreReturnValue
92+
public SpringWebfluxClientTelemetryBuilder setKnownMethods(Set<String> knownMethods) {
93+
builder.setKnownMethods(knownMethods);
94+
return this;
95+
}
96+
97+
/** Sets custom client {@link SpanNameExtractor} via transform function. */
98+
@CanIgnoreReturnValue
99+
public SpringWebfluxClientTelemetryBuilder setSpanNameExtractor(
100+
Function<
101+
SpanNameExtractor<? super ClientRequest>,
102+
? extends SpanNameExtractor<? super ClientRequest>>
103+
clientSpanNameExtractor) {
104+
builder.setSpanNameExtractor(clientSpanNameExtractor);
105+
return this;
106+
}
107+
108+
/**
109+
* Returns a new {@link SpringWebfluxClientTelemetry} with the settings of this {@link
110+
* SpringWebfluxClientTelemetryBuilder}.
111+
*/
112+
public SpringWebfluxClientTelemetry build() {
113+
return new SpringWebfluxClientTelemetry(builder.build(), openTelemetry.getPropagators());
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.webflux.v5_3;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import io.opentelemetry.instrumentation.reactor.v3_1.ContextPropagationOperator;
11+
import org.springframework.web.server.ServerWebExchange;
12+
import org.springframework.web.server.WebFilter;
13+
14+
/** Entrypoint for instrumenting Spring Webflux HTTP services. */
15+
public final class SpringWebfluxServerTelemetry {
16+
17+
/**
18+
* Returns a new {@link SpringWebfluxServerTelemetry} configured with the given {@link
19+
* OpenTelemetry}.
20+
*/
21+
public static SpringWebfluxServerTelemetry create(OpenTelemetry openTelemetry) {
22+
return builder(openTelemetry).build();
23+
}
24+
25+
/**
26+
* Returns a new {@link SpringWebfluxServerTelemetryBuilder} configured with the given {@link
27+
* OpenTelemetry}.
28+
*/
29+
public static SpringWebfluxServerTelemetryBuilder builder(OpenTelemetry openTelemetry) {
30+
return new SpringWebfluxServerTelemetryBuilder(openTelemetry);
31+
}
32+
33+
// We use ServerWebExchange (which holds both the request and response)
34+
// because we need it to get the HTTP route while instrumenting.
35+
private final Instrumenter<ServerWebExchange, ServerWebExchange> serverInstrumenter;
36+
37+
SpringWebfluxServerTelemetry(
38+
Instrumenter<ServerWebExchange, ServerWebExchange> serverInstrumenter) {
39+
this.serverInstrumenter = serverInstrumenter;
40+
}
41+
42+
public WebFilter createWebFilter() {
43+
return new TelemetryProducingWebFilter(serverInstrumenter);
44+
}
45+
46+
public WebFilter createWebFilterAndRegisterReactorHook() {
47+
registerReactorHook();
48+
return this.createWebFilter();
49+
}
50+
51+
private static void registerReactorHook() {
52+
ContextPropagationOperator.builder().build().registerOnEachOperator();
53+
}
54+
}

0 commit comments

Comments
 (0)