Browse Source

Spring Webflux Library Instrumentation (#7899)

Resolves
https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/7436

* Created new Module `spring-webflux-5.3` which contains only
server-side library instrumentation
* Minimum supported version is 5.3 because there are various problems in
older versions of reactor and webflux that prevent a few of the tests
from passing.
* Moved existing `spring-webflux-5.0` (webclient instrumentation) into a
common `spring-webflux` folder next to the 5.3 (server) instrumentation.
Moved the README to the parent folder so the docs are cohesive between
client/server instrumentation.
* Implemented `WebFilter` which instruments the server-side 
* Depends on the `reactor-3.1` instrumentation to pass the context
around. Registers the react hook when it creates the `WebFilter`
* Tests using the standard HTTP server test suite

---------

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
James Moessis 2 years ago
parent
commit
3f45f755a9
67 changed files with 895 additions and 206 deletions
  1. 1 1
      docs/supported-libraries.md
  2. 12 2
      instrumentation/spring/spring-boot-autoconfigure/README.md
  3. 1 1
      instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts
  4. 1 1
      instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/httpclients/webclient/WebClientBeanPostProcessor.java
  5. 0 38
      instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java
  6. 0 97
      instrumentation/spring/spring-webflux-5.0/library/README.md
  7. 2 2
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/build.gradle.kts
  8. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/SpringWebfluxConfig.java
  9. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientBuilderInstrumentation.java
  10. 47 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java
  11. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebfluxClientInstrumentationModule.java
  12. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/AdviceUtils.java
  13. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/DispatcherHandlerInstrumentation.java
  14. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/ExperimentalAttributesExtractor.java
  15. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerAdapterInstrumentation.java
  16. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerCodeAttributesGetter.java
  17. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouteOnSuccess.java
  18. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouterFunctionInstrumentation.java
  19. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxServerInstrumentationModule.java
  20. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSingletons.java
  21. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSpanNameExtractor.java
  22. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/SingleThreadedSpringWebfluxTest.groovy
  23. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ControllerSpringWebFluxServerTest.groovy
  24. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedControllerSpringWebFluxServerTest.groovy
  25. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedHandlerSpringWebFluxServerTest.groovy
  26. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy
  27. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateControllerSpringWebFluxServerTest.groovy
  28. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateHandlerSpringWebFluxServerTest.groovy
  29. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/SpringWebFluxServerTest.groovy
  30. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/package-info.groovy
  31. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/SpringWebfluxClientInstrumentationTest.java
  32. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/SpringWebfluxTest.java
  33. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandler.java
  34. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandlerFunction.java
  35. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/FooModel.java
  36. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/RedirectComponent.java
  37. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/SpringWebFluxTestApplication.java
  38. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/TestController.java
  39. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestController.java
  40. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestRouteFactory.java
  41. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/resources/logback.xml
  42. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/NOTICE.txt
  43. 82 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/README.md
  44. 16 5
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/build.gradle.kts
  45. 28 5
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetry.java
  46. 156 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java
  47. 141 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/TelemetryProducingWebFilter.java
  48. 89 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxServerHttpAttributesGetter.java
  49. 46 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxServerNetAttributesGetter.java
  50. 28 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxTextMapGetter.java
  51. 1 1
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/HttpHeadersSetter.java
  52. 39 34
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxTelemetryClientBuilder.java
  53. 1 1
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/StatusCodes.java
  54. 1 1
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/TraceWebClientSubscriber.java
  55. 2 2
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientExperimentalAttributesExtractor.java
  56. 2 2
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientHttpAttributesGetter.java
  57. 2 2
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientNetAttributesGetter.java
  58. 5 2
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientTracingFilter.java
  59. 3 3
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxClientInstrumentationTest.java
  60. 48 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java
  61. 133 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/TestWebfluxSpringBootApp.java
  62. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/build.gradle.kts
  63. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/AbstractSpringWebfluxClientInstrumentationTest.java
  64. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/ClientHttpConnectorFactory.java
  65. 0 0
      instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxSingleConnection.java
  66. 3 3
      settings.gradle.kts
  67. 5 3
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java

+ 1 - 1
docs/supported-libraries.md

@@ -119,7 +119,7 @@ These are the supported libraries and frameworks:
 | [Spring RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/package-summary.html) | 3.1+                          | [opentelemetry-spring-web-3.1](../instrumentation/spring/spring-web/spring-web-3.1/library)                                                                                                                                                                                                                                                                                             | [HTTP Client Spans], [HTTP Client Metrics]                                             |
 | [Spring Web MVC](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/package-summary.html)           | 3.1+                          | [opentelemetry-spring-webmvc-5.3](../instrumentation/spring/spring-webmvc/spring-webmvc-5.3/library),<br>[opentelemetry-spring-webmvc-6.0](../instrumentation/spring/spring-webmvc/spring-webmvc-6.0/library)                                                                                                                                                                           | [HTTP Server Spans], [HTTP Server Metrics]                                             |
 | [Spring Web Services](https://spring.io/projects/spring-ws)                                                                                 | 2.0+                          | N/A                                                                                                                                                                                                                                                                                                                                                                                     | none                                                                                   |
-| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html)              | 5.0+                          | [opentelemetry-spring-webflux-5.0](../instrumentation/spring/spring-webflux-5.0/library)                                                                                                                                                                                                                                                                                                | [HTTP Client Spans], [HTTP Client Metrics],                                            |
+| [Spring WebFlux](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/package-summary.html)              | 5.3+                          | [opentelemetry-spring-webflux-5.3](../instrumentation/spring/spring-webflux/spring-webflux-5.3/library)                                                                                                                                                                                                                                                                                 | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans]                        |
 | [Spymemcached](https://github.com/couchbase/spymemcached)                                                                                   | 2.12+                         | N/A                                                                                                                                                                                                                                                                                                                                                                                     | [Database Client Spans]                                                                |
 | [Tomcat JDBC Pool](https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html)                                                                 | 8.5.0+                        | N/A                                                                                                                                                                                                                                                                                                                                                                                     | [Database Pool Metrics]                                                                |
 | [Twilio](https://github.com/twilio/twilio-java)                                                                                             | 6.6+ (not including 8.x yet)  | N/A                                                                                                                                                                                                                                                                                                                                                                                     | none                                                                                   |

+ 12 - 2
instrumentation/spring/spring-boot-autoconfigure/README.md

@@ -2,7 +2,8 @@
 
 Auto-configures OpenTelemetry instrumentation for [spring-web](../spring-web/spring-web-3.1/library)
 , [spring-webmvc](../spring-webmvc/spring-webmvc-5.3/library),
-and [spring-webflux](../spring-webflux-5.0/library). Leverages Spring Aspect Oriented Programming,
+and [spring-webflux](../spring-webflux/spring-webflux-5.3/library). Leverages Spring Aspect Oriented
+Programming,
 dependency injection, and bean post-processing to trace spring applications. To include all features
 listed below use the [opentelemetry-spring-boot-starter](../starters/spring-boot-starter/README.md).
 
@@ -167,7 +168,16 @@ to learn more about the OpenTelemetry Spring WebMVC instrumentation.
 
 #### Spring WebFlux Auto Configuration
 
-Provides auto-configurations for the OpenTelemetry WebClient ExchangeFilter defined in [opentelemetry-spring-webflux-5.0](../spring-webflux-5.0). This auto-configuration instruments all outgoing http requests sent using Spring's WebClient and WebClient Builder beans by applying a bean post processor. This feature is supported for spring webflux versions 5.0+ and can be disabled by adding `opentelemetry.trace.httpclients.enabled=false` to your `resources/applications.properties` file. [Spring Web-Flux - WebClient Span](#spring-web-flux---webclient-span) showcases a sample span generated by the WebClientFilter. Check out [opentelemetry-spring-webflux-5.0](../spring-webflux-5.0) to learn more about the OpenTelemetry WebClientFilter.
+Provides auto-configurations for the OpenTelemetry WebClient ExchangeFilter defined
+in [opentelemetry-spring-webflux-5.3](../spring-webflux/spring-webflux-5.3). This auto-configuration
+instruments all outgoing http requests sent using Spring's WebClient and WebClient Builder beans by
+applying a bean post processor. This feature is supported for spring webflux versions 5.0+ and can
+be disabled by adding `opentelemetry.trace.httpclients.enabled=false` to
+your `resources/applications.properties`
+file. [Spring Web-Flux - WebClient Span](#spring-web-flux---webclient-span) showcases a sample span
+generated by the WebClientFilter. Check
+out [opentelemetry-spring-webflux-5.3](../spring-webflux/spring-webflux-5.3) to learn more about the
+OpenTelemetry WebClientFilter.
 
 #### Manual Instrumentation Support - @WithSpan
 

+ 1 - 1
instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts

@@ -21,7 +21,7 @@ dependencies {
   implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-5.3:library"))
   implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
   compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0")
-  implementation(project(":instrumentation:spring:spring-webflux-5.0:library"))
+  implementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.3:library"))
   implementation(project(":instrumentation:micrometer:micrometer-1.5:library"))
 
   compileOnly("org.springframework.kafka:spring-kafka:2.9.0")

+ 1 - 1
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/httpclients/webclient/WebClientBeanPostProcessor.java

@@ -6,7 +6,7 @@
 package io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient;
 
 import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.instrumentation.spring.webflux.v5_0.client.SpringWebfluxTelemetry;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.web.reactive.function.client.WebClient;

+ 0 - 38
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java

@@ -1,38 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.client;
-
-import io.opentelemetry.api.GlobalOpenTelemetry;
-import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor;
-import io.opentelemetry.instrumentation.spring.webflux.v5_0.client.SpringWebfluxTelemetry;
-import io.opentelemetry.instrumentation.spring.webflux.v5_0.client.internal.SpringWebfluxNetAttributesGetter;
-import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
-import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
-import java.util.List;
-import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
-
-public final class WebClientHelper {
-
-  private static final SpringWebfluxTelemetry INSTRUMENTATION =
-      SpringWebfluxTelemetry.builder(GlobalOpenTelemetry.get())
-          .setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders())
-          .setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders())
-          .addAttributesExtractor(
-              PeerServiceAttributesExtractor.create(
-                  new SpringWebfluxNetAttributesGetter(),
-                  CommonConfig.get().getPeerServiceMapping()))
-          .setCaptureExperimentalSpanAttributes(
-              InstrumentationConfig.get()
-                  .getBoolean(
-                      "otel.instrumentation.spring-webflux.experimental-span-attributes", false))
-          .build();
-
-  public static void addFilter(List<ExchangeFilterFunction> exchangeFilterFunctions) {
-    INSTRUMENTATION.addClientTracingFilter(exchangeFilterFunctions);
-  }
-
-  private WebClientHelper() {}
-}

+ 0 - 97
instrumentation/spring/spring-webflux-5.0/library/README.md

@@ -1,97 +0,0 @@
-# Library Instrumentation for Spring Webflux version 5.0 and higher
-
-Provides OpenTelemetry instrumentation for Spring's `WebClient`.
-
-## Quickstart
-
-### Add these dependencies to your project
-
-Replace `SPRING_VERSION` with the version of spring you're using.
-`Minimum version: 5.0`
-
-Replace `OPENTELEMETRY_VERSION` with the [latest
-release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-spring-webflux-5.0).
-
-For Maven, add to your `pom.xml` dependencies:
-
-```xml
-<dependencies>
-  <!-- opentelemetry instrumentation -->
-  <dependency>
-    <groupId>io.opentelemetry.instrumentation</groupId>
-    <artifactId>opentelemetry-spring-webflux-5.0</artifactId>
-    <version>OPENTELEMETRY_VERSION</version>
-  </dependency>
-
-   <!-- opentelemetry exporter -->
-   <!-- replace this default exporter with your opentelemetry exporter (ex. otlp/zipkin/jaeger/..) -->
-   <dependency>
-    <groupId>io.opentelemetry</groupId>
-    <artifactId>opentelemetry-exporter-logging</artifactId>
-    <version>OPENTELEMETRY_VERSION</version>
-  </dependency>
-
-  <!-- required to instrument spring-webflux -->
-  <!-- this artifact should already be present in your application -->
-  <dependency>
-    <groupId>org.springframework</groupId>
-    <artifactId>spring-webflux</artifactId>
-    <version>SPRING_VERSION</version>
-  </dependency>
-
-</dependencies>
-```
-
-For Gradle, add to your dependencies:
-
-```groovy
-// opentelemetry instrumentation
-implementation("io.opentelemetry.instrumentation:opentelemetry-spring-webflux-5.0:OPENTELEMETRY_VERSION")
-
-// opentelemetry exporter
-// replace this default exporter with your opentelemetry exporter (ex. otlp/zipkin/jaeger/..)
-implementation("io.opentelemetry:opentelemetry-exporter-logging:OPENTELEMETRY_VERSION")
-
-// required to instrument spring-webmvc
-// this artifact should already be present in your application
-implementation("org.springframework:spring-webflux:SPRING_VERSION")
-```
-
-### Features
-
-#### `SpringWebfluxTracing`
-
-`SpringWebfluxTracing` emits client span for each request sent using `WebClient` by implementing
-the [ExchangeFilterFunction](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ExchangeFilterFunction.html)
-interface. An example is shown below:
-
-##### Usage
-
-```java
-
-import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.instrumentation.spring.webflux.client.SpringWebfluxTracing;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.reactive.function.client.WebClient;
-
-@Configuration
-public class WebClientConfig {
-
-   @Bean
-   public WebClient.Builder webClient(OpenTelemetry openTelemetry) {
-
-      WebClient webClient = WebClient.create();
-      SpringWebfluxTracing instrumentation = SpringWebfluxTracing.create(openTelemetry);
-
-      return webClient.mutate().filters(instrumentation::addClientTracingFilter);
-   }
-}
-```
-
-### Starter Guide
-
-Check out [OpenTelemetry Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/) to learn more about
-using the OpenTelemetry API to instrument your code.

+ 2 - 2
instrumentation/spring/spring-webflux-5.0/javaagent/build.gradle.kts → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/build.gradle.kts

@@ -39,7 +39,7 @@ muzzle {
 }
 
 dependencies {
-  implementation(project(":instrumentation:spring:spring-webflux-5.0:library"))
+  implementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.3:library"))
 
   compileOnly("org.springframework:spring-webflux:5.0.0.RELEASE")
   compileOnly("io.projectreactor.ipc:reactor-netty:0.7.0.RELEASE")
@@ -51,7 +51,7 @@ dependencies {
   testInstrumentation(project(":instrumentation:reactor:reactor-3.1:javaagent"))
   testInstrumentation(project(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent"))
 
-  testImplementation(project(":instrumentation:spring:spring-webflux-5.0:testing"))
+  testImplementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.3:testing"))
 
   testLibrary("org.springframework.boot:spring-boot-starter-webflux:2.0.0.RELEASE")
   testLibrary("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE")

+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/SpringWebfluxConfig.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/SpringWebfluxConfig.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientBuilderInstrumentation.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientBuilderInstrumentation.java


+ 47 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebClientHelper.java

@@ -0,0 +1,47 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.client;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxTelemetryClientBuilder;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientNetAttributesGetter;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientTracingFilter;
+import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
+import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
+import java.util.List;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+
+public final class WebClientHelper {
+
+  private static final Instrumenter<ClientRequest, ClientResponse> instrumenter =
+      new SpringWebfluxTelemetryClientBuilder(GlobalOpenTelemetry.get())
+          .setCapturedClientRequestHeaders(CommonConfig.get().getClientRequestHeaders())
+          .setCapturedClientResponseHeaders(CommonConfig.get().getClientResponseHeaders())
+          .addClientAttributesExtractor(
+              PeerServiceAttributesExtractor.create(
+                  new WebClientNetAttributesGetter(), CommonConfig.get().getPeerServiceMapping()))
+          .setCaptureExperimentalSpanAttributes(
+              InstrumentationConfig.get()
+                  .getBoolean(
+                      "otel.instrumentation.spring-webflux.experimental-span-attributes", false))
+          .build();
+
+  public static void addFilter(List<ExchangeFilterFunction> exchangeFilterFunctions) {
+    for (ExchangeFilterFunction filterFunction : exchangeFilterFunctions) {
+      if (filterFunction instanceof WebClientTracingFilter) {
+        return;
+      }
+    }
+    exchangeFilterFunctions.add(
+        new WebClientTracingFilter(instrumenter, GlobalOpenTelemetry.get().getPropagators()));
+  }
+
+  private WebClientHelper() {}
+}

+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebfluxClientInstrumentationModule.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/WebfluxClientInstrumentationModule.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/AdviceUtils.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/AdviceUtils.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/DispatcherHandlerInstrumentation.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/DispatcherHandlerInstrumentation.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/ExperimentalAttributesExtractor.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/ExperimentalAttributesExtractor.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerAdapterInstrumentation.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerAdapterInstrumentation.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerCodeAttributesGetter.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/HandlerCodeAttributesGetter.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouteOnSuccess.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouteOnSuccess.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouterFunctionInstrumentation.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/RouterFunctionInstrumentation.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxServerInstrumentationModule.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxServerInstrumentationModule.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSingletons.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSingletons.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSpanNameExtractor.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/WebfluxSpanNameExtractor.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/SingleThreadedSpringWebfluxTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/SingleThreadedSpringWebfluxTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ControllerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ControllerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedControllerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedControllerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedHandlerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/DelayedHandlerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/HandlerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateControllerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateControllerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateHandlerSpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/ImmediateHandlerSpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/SpringWebFluxServerTest.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/SpringWebFluxServerTest.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/groovy/server/base/package-info.groovy → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/groovy/server/base/package-info.groovy


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/SpringWebfluxClientInstrumentationTest.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/client/SpringWebfluxClientInstrumentationTest.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/SpringWebfluxTest.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/SpringWebfluxTest.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandler.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandler.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandlerFunction.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/EchoHandlerFunction.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/FooModel.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/FooModel.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/RedirectComponent.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/RedirectComponent.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/SpringWebFluxTestApplication.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/SpringWebFluxTestApplication.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/TestController.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/TestController.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestController.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestController.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestRouteFactory.java → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/java/server/base/ServerTestRouteFactory.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/javaagent/src/test/resources/logback.xml → instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/test/resources/logback.xml


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/library/NOTICE.txt → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/NOTICE.txt


+ 82 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/README.md

@@ -0,0 +1,82 @@
+# Library Instrumentation for Spring Webflux
+
+Provides OpenTelemetry instrumentation for Spring's `WebClient` and Webflux server.
+
+For this instrumentation, the minimum supported version of Spring Webflux is 5.3.0.
+
+## Add dependencies to your project
+
+For Maven, add to your `pom.xml`:
+
+```xml
+<dependencies>
+  <dependency>
+    <groupId>io.opentelemetry.instrumentation</groupId>
+    <artifactId>opentelemetry-spring-webflux-5.3</artifactId>
+    <version>OPENTELEMETRY_VERSION</version>
+  </dependency>
+
+  <!-- This artifact should already be present in your application -->
+  <dependency>
+    <groupId>org.springframework</groupId>
+    <artifactId>spring-webflux</artifactId>
+    <version>SPRING_VERSION</version>
+  </dependency>
+</dependencies>
+```
+
+For Gradle, add to your dependencies:
+
+```groovy
+implementation("io.opentelemetry.instrumentation:opentelemetry-spring-webflux-5.3:OPENTELEMETRY_VERSION")
+
+// this artifact should already be present in your application
+implementation("org.springframework:spring-webflux:SPRING_VERSION")
+```
+
+## Features
+
+`SpringWebfluxTelemetry` can emit a client span for each request sent using `WebClient` by
+implementing
+the [ExchangeFilterFunction](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ExchangeFilterFunction.html)
+interface.
+
+`SpringWebfluxTelemetry` can also emit a server span for each request received, by implementing
+a `WebFilter` and using the OpenTelemetry Reactor instrumentation to ensure context is
+passed around correctly.
+
+### Setup
+
+Here is how to set up client and server instrumentation respectively:
+
+```java
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
+
+@Configuration
+public class WebClientConfig {
+  private final SpringWebfluxTelemetry webfluxTelemetry;
+
+  public WebClientConfig(OpenTelemetry openTelemetry) {
+    this.webfluxTelemetry = SpringWebfluxTelemetry.builder(openTelemetry).build();
+  }
+
+  // Adds instrumentation to WebClients
+  @Bean
+  public WebClient.Builder webClient() {
+    WebClient webClient = WebClient.create();
+    return webClient.mutate().filters(webfluxTelemetry::addClientTracingFilter);
+  }
+
+  // Adds instrumentation to Webflux server
+  @Bean
+  public WebFilter webFilter() {
+    return webfluxTelemetry.createWebFilterAndRegisterReactorHook();
+  }
+}
+```
+
+## Starter Guide
+
+Check
+out [OpenTelemetry Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/java/manual/)
+to learn more about using the OpenTelemetry API to instrument your code.

+ 16 - 5
instrumentation/spring/spring-webflux-5.0/library/build.gradle.kts → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/build.gradle.kts

@@ -3,15 +3,17 @@ plugins {
 }
 
 dependencies {
-  library("org.springframework:spring-webflux:5.0.0.RELEASE")
+  library("org.springframework:spring-webflux:5.3.0")
+
+  implementation(project(":instrumentation:reactor:reactor-3.1:library"))
 
   compileOnly("io.projectreactor.ipc:reactor-netty:0.7.0.RELEASE")
 
-  testImplementation(project(":instrumentation:spring:spring-webflux-5.0:testing"))
+  testImplementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.3:testing"))
 
-  testLibrary("org.springframework.boot:spring-boot-starter-webflux:2.0.0.RELEASE")
-  testLibrary("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE")
-  testLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:2.0.0.RELEASE")
+  testLibrary("org.springframework.boot:spring-boot-starter-webflux:2.4.0")
+  testLibrary("org.springframework.boot:spring-boot-starter-test:2.4.0")
+  testLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:2.4.0")
 }
 
 val latestDepTest = findProperty("testLatestDeps") as Boolean
@@ -22,3 +24,12 @@ if (latestDepTest) {
     minJavaVersionSupported.set(JavaVersion.VERSION_17)
   }
 }
+
+if (!latestDepTest) {
+  // Spring Boot 2.x requires StaticLoggerBinder which is removed in logback-classic 1.3
+  configurations.testRuntimeClasspath {
+    resolutionStrategy {
+      force("ch.qos.logback:logback-classic:1.2.3")
+    }
+  }
+}

+ 28 - 5
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/SpringWebfluxTelemetry.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetry.java

@@ -3,15 +3,19 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
 
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.context.propagation.ContextPropagators;
 import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.reactor.v3_1.ContextPropagationOperator;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.WebClientTracingFilter;
 import java.util.List;
 import org.springframework.web.reactive.function.client.ClientRequest;
 import org.springframework.web.reactive.function.client.ClientResponse;
 import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
 
 /** Entrypoint for instrumenting Spring Webflux HTTP clients. */
 public final class SpringWebfluxTelemetry {
@@ -31,12 +35,18 @@ public final class SpringWebfluxTelemetry {
     return new SpringWebfluxTelemetryBuilder(openTelemetry);
   }
 
-  private final Instrumenter<ClientRequest, ClientResponse> instrumenter;
+  // We use ServerWebExchange (which holds both the request and response)
+  // because we need it to get the HTTP route while instrumenting.
+  private final Instrumenter<ServerWebExchange, ServerWebExchange> serverInstrumenter;
+  private final Instrumenter<ClientRequest, ClientResponse> clientInstrumenter;
   private final ContextPropagators propagators;
 
   SpringWebfluxTelemetry(
-      Instrumenter<ClientRequest, ClientResponse> instrumenter, ContextPropagators propagators) {
-    this.instrumenter = instrumenter;
+      Instrumenter<ClientRequest, ClientResponse> clientInstrumenter,
+      Instrumenter<ServerWebExchange, ServerWebExchange> serverInstrumenter,
+      ContextPropagators propagators) {
+    this.clientInstrumenter = clientInstrumenter;
+    this.serverInstrumenter = serverInstrumenter;
     this.propagators = propagators;
   }
 
@@ -46,6 +56,19 @@ public final class SpringWebfluxTelemetry {
         return;
       }
     }
-    exchangeFilterFunctions.add(new WebClientTracingFilter(instrumenter, propagators));
+    exchangeFilterFunctions.add(new WebClientTracingFilter(clientInstrumenter, propagators));
+  }
+
+  public WebFilter createWebFilter() {
+    return new TelemetryProducingWebFilter(serverInstrumenter);
+  }
+
+  public WebFilter createWebFilterAndRegisterReactorHook() {
+    registerReactorHook();
+    return this.createWebFilter();
+  }
+
+  private static void registerReactorHook() {
+    ContextPropagationOperator.builder().build().registerOnEachOperator();
   }
 }

+ 156 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxTelemetryBuilder.java

@@ -0,0 +1,156 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractorBuilder;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.internal.SpringWebfluxTelemetryClientBuilder;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.server.ServerWebExchange;
+
+/** A builder of {@link SpringWebfluxTelemetry}. */
+public final class SpringWebfluxTelemetryBuilder {
+  private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webflux-5.3";
+
+  private final OpenTelemetry openTelemetry;
+
+  private final SpringWebfluxTelemetryClientBuilder clientBuilder;
+
+  private final List<AttributesExtractor<ServerWebExchange, ServerWebExchange>>
+      serverAdditionalExtractors = new ArrayList<>();
+  private final HttpServerAttributesExtractorBuilder<ServerWebExchange, ServerWebExchange>
+      httpServerAttributesExtractorBuilder =
+          HttpServerAttributesExtractor.builder(
+              WebfluxServerHttpAttributesGetter.INSTANCE, new WebfluxServerNetAttributesGetter());
+
+  SpringWebfluxTelemetryBuilder(OpenTelemetry openTelemetry) {
+    this.openTelemetry = openTelemetry;
+    clientBuilder = new SpringWebfluxTelemetryClientBuilder(openTelemetry);
+  }
+
+  /**
+   * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
+   * items for WebClient.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder addClientAttributesExtractor(
+      AttributesExtractor<ClientRequest, ClientResponse> attributesExtractor) {
+    clientBuilder.addClientAttributesExtractor(attributesExtractor);
+    return this;
+  }
+
+  /**
+   * Configures the HTTP WebClient request headers that will be captured as span attributes.
+   *
+   * @param requestHeaders A list of HTTP header names.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder setCapturedClientRequestHeaders(
+      List<String> requestHeaders) {
+    clientBuilder.setCapturedClientRequestHeaders(requestHeaders);
+    return this;
+  }
+
+  /**
+   * Configures the HTTP WebClient response headers that will be captured as span attributes.
+   *
+   * @param responseHeaders A list of HTTP header names.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder setCapturedClientResponseHeaders(
+      List<String> responseHeaders) {
+    clientBuilder.setCapturedClientResponseHeaders(responseHeaders);
+    return this;
+  }
+
+  /**
+   * Sets whether experimental attributes should be set to spans. These attributes may be changed or
+   * removed in the future, so only enable this if you know you do not require attributes filled by
+   * this instrumentation to be stable across versions.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder setCaptureExperimentalSpanAttributes(
+      boolean captureExperimentalSpanAttributes) {
+    clientBuilder.setCaptureExperimentalSpanAttributes(captureExperimentalSpanAttributes);
+    return this;
+  }
+
+  /**
+   * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
+   * items.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder addServerAttributesExtractor(
+      AttributesExtractor<ServerWebExchange, ServerWebExchange> attributesExtractor) {
+    serverAdditionalExtractors.add(attributesExtractor);
+    return this;
+  }
+
+  /**
+   * Configures the HTTP request headers that will be captured as span attributes from server
+   * instrumentation.
+   *
+   * @param requestHeaders A list of HTTP header names.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder setCapturedServerRequestHeaders(
+      List<String> requestHeaders) {
+    httpServerAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
+    return this;
+  }
+
+  /**
+   * Configures the HTTP response headers that will be captured as span attributes from server
+   * instrumentation.
+   *
+   * @param responseHeaders A list of HTTP header names.
+   */
+  @CanIgnoreReturnValue
+  public SpringWebfluxTelemetryBuilder setCapturedServerResponseHeaders(
+      List<String> responseHeaders) {
+    httpServerAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
+    return this;
+  }
+
+  /**
+   * Returns a new {@link SpringWebfluxTelemetry} with the settings of this {@link
+   * SpringWebfluxTelemetryBuilder}.
+   */
+  public SpringWebfluxTelemetry build() {
+
+    Instrumenter<ClientRequest, ClientResponse> clientInstrumenter = clientBuilder.build();
+
+    WebfluxServerHttpAttributesGetter serverAttributesGetter =
+        WebfluxServerHttpAttributesGetter.INSTANCE;
+    SpanNameExtractor<ServerWebExchange> serverSpanNameExtractor =
+        HttpSpanNameExtractor.create(serverAttributesGetter);
+
+    Instrumenter<ServerWebExchange, ServerWebExchange> serverInstrumenter =
+        Instrumenter.<ServerWebExchange, ServerWebExchange>builder(
+                openTelemetry, INSTRUMENTATION_NAME, serverSpanNameExtractor)
+            .setSpanStatusExtractor(HttpSpanStatusExtractor.create(serverAttributesGetter))
+            .addAttributesExtractor(httpServerAttributesExtractorBuilder.build())
+            .addAttributesExtractors(serverAdditionalExtractors)
+            .addContextCustomizer(HttpRouteHolder.create(serverAttributesGetter))
+            .addOperationMetrics(HttpServerMetrics.get())
+            .buildServerInstrumenter(WebfluxTextMapGetter.INSTANCE);
+
+    return new SpringWebfluxTelemetry(
+        clientInstrumenter, serverInstrumenter, openTelemetry.getPropagators());
+  }
+}

+ 141 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/TelemetryProducingWebFilter.java

@@ -0,0 +1,141 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteHolder;
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpRouteSource;
+import org.reactivestreams.Subscription;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.CoreSubscriber;
+import reactor.core.publisher.Mono;
+import reactor.util.annotation.NonNull;
+
+final class TelemetryProducingWebFilter implements WebFilter, Ordered {
+
+  private final Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter;
+
+  TelemetryProducingWebFilter(Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter) {
+    this.instrumenter = instrumenter;
+  }
+
+  @Override
+  @NonNull
+  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+    Context parentContext = Context.current();
+    Mono<Void> source = chain.filter(exchange);
+    return new TelemetryWrappedMono(source, instrumenter, parentContext, exchange);
+  }
+
+  @Override
+  public int getOrder() {
+    return Ordered.HIGHEST_PRECEDENCE + 1;
+  }
+
+  private static class TelemetryWrappedMono extends Mono<Void> {
+
+    private final Mono<Void> source;
+    private final Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter;
+    private final Context parentContext;
+    private final ServerWebExchange exchange;
+
+    TelemetryWrappedMono(
+        Mono<Void> source,
+        Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter,
+        Context parentContext,
+        ServerWebExchange exchange) {
+      this.source = source;
+      this.instrumenter = instrumenter;
+      this.parentContext = parentContext;
+      this.exchange = exchange;
+    }
+
+    @Override
+    public void subscribe(CoreSubscriber<? super Void> actual) {
+      if (!instrumenter.shouldStart(parentContext, exchange)) {
+        source.subscribe(actual);
+        return;
+      }
+      Context currentContext = instrumenter.start(parentContext, exchange);
+      try (Scope ignored = currentContext.makeCurrent()) {
+        this.source.subscribe(
+            new TelemetryWrappedSubscriber(actual, currentContext, instrumenter, exchange));
+      }
+    }
+  }
+
+  private static class TelemetryWrappedSubscriber implements CoreSubscriber<Void> {
+    private final CoreSubscriber<? super Void> actual;
+    private final Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter;
+    private final Context currentOtelContext;
+    private final ServerWebExchange exchange;
+
+    TelemetryWrappedSubscriber(
+        CoreSubscriber<? super Void> actual,
+        Context currentOtelContext,
+        Instrumenter<ServerWebExchange, ServerWebExchange> instrumenter,
+        ServerWebExchange exchange) {
+      this.actual = actual;
+      this.instrumenter = instrumenter;
+      this.currentOtelContext = currentOtelContext;
+      this.exchange = exchange;
+    }
+
+    @Override
+    public reactor.util.context.Context currentContext() {
+      return actual.currentContext();
+    }
+
+    @Override
+    public void onSubscribe(Subscription s) {
+      actual.onSubscribe(s);
+    }
+
+    @Override
+    public void onNext(Void unused) {}
+
+    @Override
+    public void onError(Throwable t) {
+      onTerminal(currentOtelContext, t);
+      actual.onError(t);
+    }
+
+    @Override
+    public void onComplete() {
+      onTerminal(currentOtelContext, null);
+      actual.onComplete();
+    }
+
+    private void onTerminal(Context currentContext, Throwable t) {
+      ServerHttpResponse response = exchange.getResponse();
+      if (response.isCommitted()) {
+        end(currentContext, t);
+      } else {
+        response.beforeCommit(
+            () -> {
+              end(currentContext, t);
+              return Mono.empty();
+            });
+      }
+    }
+
+    private void end(Context currentContext, Throwable t) {
+      // Update HTTP route now, because during instrumenter.start()
+      // the HTTP route isn't available from the exchange attributes, but is afterwards
+      HttpRouteHolder.updateHttpRoute(
+          currentContext,
+          HttpRouteSource.CONTROLLER,
+          WebfluxServerHttpAttributesGetter.INSTANCE.getRoute(exchange));
+      instrumenter.end(currentContext, exchange, exchange, t);
+    }
+  }
+}

+ 89 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxServerHttpAttributesGetter.java

@@ -0,0 +1,89 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesGetter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.springframework.web.reactive.HandlerMapping;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.pattern.PathPattern;
+
+enum WebfluxServerHttpAttributesGetter
+    implements HttpServerAttributesGetter<ServerWebExchange, ServerWebExchange> {
+  INSTANCE;
+
+  @Override
+  public String getMethod(ServerWebExchange request) {
+    return request.getRequest().getMethodValue();
+  }
+
+  @Override
+  public List<String> getRequestHeader(ServerWebExchange request, String name) {
+    return request.getRequest().getHeaders().getOrDefault(name, Collections.emptyList());
+  }
+
+  @Nullable
+  @Override
+  public Integer getStatusCode(
+      ServerWebExchange request, ServerWebExchange response, @Nullable Throwable error) {
+    return response.getResponse().getRawStatusCode();
+  }
+
+  @Override
+  public List<String> getResponseHeader(
+      ServerWebExchange request, ServerWebExchange response, String name) {
+    return response.getResponse().getHeaders().getOrDefault(name, Collections.emptyList());
+  }
+
+  @Nullable
+  @Override
+  public String getFlavor(ServerWebExchange request) {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public String getTarget(ServerWebExchange request) {
+    String path = request.getRequest().getURI().getPath();
+    String query = request.getRequest().getURI().getQuery();
+    if (path == null && query == null) {
+      return null;
+    }
+    if (query != null) {
+      query = "?" + query;
+    }
+    return Optional.ofNullable(path).orElse("") + Optional.ofNullable(query).orElse("");
+  }
+
+  @Nullable
+  @Override
+  public String getRoute(ServerWebExchange request) {
+    Object bestPatternObj = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
+    if (bestPatternObj == null) {
+      return null;
+    }
+    String route;
+    if (bestPatternObj instanceof PathPattern) {
+      route = ((PathPattern) bestPatternObj).getPatternString();
+    } else {
+      route = bestPatternObj.toString();
+    }
+    if (route.equals("/**")) {
+      return null;
+    }
+    String contextPath = request.getRequest().getPath().contextPath().value();
+    return contextPath + (route.startsWith("/") ? route : ("/" + route));
+  }
+
+  @Nullable
+  @Override
+  public String getScheme(ServerWebExchange request) {
+    return request.getRequest().getURI().getScheme();
+  }
+}

+ 46 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxServerNetAttributesGetter.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetServerAttributesGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import java.net.InetSocketAddress;
+import javax.annotation.Nullable;
+import org.springframework.web.server.ServerWebExchange;
+
+final class WebfluxServerNetAttributesGetter
+    extends InetSocketAddressNetServerAttributesGetter<ServerWebExchange> {
+
+  @Override
+  public String getTransport(ServerWebExchange request) {
+    return SemanticAttributes.NetTransportValues.IP_TCP;
+  }
+
+  @Nullable
+  @Override
+  public String getHostName(ServerWebExchange request) {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public Integer getHostPort(ServerWebExchange request) {
+    int port = request.getRequest().getURI().getPort();
+    return port == -1 ? null : port;
+  }
+
+  @Nullable
+  @Override
+  protected InetSocketAddress getPeerSocketAddress(ServerWebExchange request) {
+    return request.getRequest().getRemoteAddress();
+  }
+
+  @Nullable
+  @Override
+  protected InetSocketAddress getHostSocketAddress(ServerWebExchange request) {
+    return request.getRequest().getLocalAddress();
+  }
+}

+ 28 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/WebfluxTextMapGetter.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import io.opentelemetry.context.propagation.TextMapGetter;
+import javax.annotation.Nullable;
+import org.springframework.web.server.ServerWebExchange;
+
+enum WebfluxTextMapGetter implements TextMapGetter<ServerWebExchange> {
+  INSTANCE;
+
+  @Override
+  public Iterable<String> keys(ServerWebExchange exchange) {
+    return exchange.getRequest().getHeaders().keySet();
+  }
+
+  @Nullable
+  @Override
+  public String get(@Nullable ServerWebExchange exchange, String key) {
+    if (exchange == null) {
+      return null;
+    }
+    return exchange.getRequest().getHeaders().getFirst(key);
+  }
+}

+ 1 - 1
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/HttpHeadersSetter.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/HttpHeadersSetter.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import io.opentelemetry.context.propagation.TextMapSetter;
 import org.springframework.web.reactive.function.client.ClientRequest;

+ 39 - 34
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/SpringWebfluxTelemetryBuilder.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxTelemetryClientBuilder.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import static io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor.alwaysClient;
 
@@ -17,59 +17,67 @@ import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttribut
 import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics;
 import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
 import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
-import io.opentelemetry.instrumentation.spring.webflux.v5_0.client.internal.SpringWebfluxNetAttributesGetter;
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
 import java.util.ArrayList;
 import java.util.List;
 import org.springframework.web.reactive.function.client.ClientRequest;
 import org.springframework.web.reactive.function.client.ClientResponse;
 
-/** A builder of {@link SpringWebfluxTelemetry}. */
-public final class SpringWebfluxTelemetryBuilder {
+/**
+ * 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 SpringWebfluxTelemetryClientBuilder {
+  private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-webflux-5.3";
 
   private final OpenTelemetry openTelemetry;
-  private final List<AttributesExtractor<ClientRequest, ClientResponse>> additionalExtractors =
-      new ArrayList<>();
+  private final List<AttributesExtractor<ClientRequest, ClientResponse>>
+      clientAdditionalExtractors = new ArrayList<>();
   private final HttpClientAttributesExtractorBuilder<ClientRequest, ClientResponse>
-      httpAttributesExtractorBuilder =
+      httpClientAttributesExtractorBuilder =
           HttpClientAttributesExtractor.builder(
-              SpringWebfluxHttpAttributesGetter.INSTANCE, new SpringWebfluxNetAttributesGetter());
+              WebClientHttpAttributesGetter.INSTANCE, new WebClientNetAttributesGetter());
 
   private boolean captureExperimentalSpanAttributes = false;
 
-  SpringWebfluxTelemetryBuilder(OpenTelemetry openTelemetry) {
+  public SpringWebfluxTelemetryClientBuilder(OpenTelemetry openTelemetry) {
     this.openTelemetry = openTelemetry;
   }
 
   /**
    * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented
-   * items.
+   * items for WebClient.
    */
   @CanIgnoreReturnValue
-  public SpringWebfluxTelemetryBuilder addAttributesExtractor(
+  public SpringWebfluxTelemetryClientBuilder addClientAttributesExtractor(
       AttributesExtractor<ClientRequest, ClientResponse> attributesExtractor) {
-    additionalExtractors.add(attributesExtractor);
+    clientAdditionalExtractors.add(attributesExtractor);
     return this;
   }
 
   /**
-   * Configures the HTTP request headers that will be captured as span attributes.
+   * Configures the HTTP WebClient request headers that will be captured as span attributes.
    *
    * @param requestHeaders A list of HTTP header names.
    */
   @CanIgnoreReturnValue
-  public SpringWebfluxTelemetryBuilder setCapturedRequestHeaders(List<String> requestHeaders) {
-    httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
+  public SpringWebfluxTelemetryClientBuilder setCapturedClientRequestHeaders(
+      List<String> requestHeaders) {
+    httpClientAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders);
     return this;
   }
 
   /**
-   * Configures the HTTP response headers that will be captured as span attributes.
+   * Configures the HTTP WebClient response headers that will be captured as span attributes.
    *
    * @param responseHeaders A list of HTTP header names.
    */
   @CanIgnoreReturnValue
-  public SpringWebfluxTelemetryBuilder setCapturedResponseHeaders(List<String> responseHeaders) {
-    httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
+  public SpringWebfluxTelemetryClientBuilder setCapturedClientResponseHeaders(
+      List<String> responseHeaders) {
+    httpClientAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders);
     return this;
   }
 
@@ -79,7 +87,7 @@ public final class SpringWebfluxTelemetryBuilder {
    * this instrumentation to be stable across versions.
    */
   @CanIgnoreReturnValue
-  public SpringWebfluxTelemetryBuilder setCaptureExperimentalSpanAttributes(
+  public SpringWebfluxTelemetryClientBuilder setCaptureExperimentalSpanAttributes(
       boolean captureExperimentalSpanAttributes) {
     this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
     return this;
@@ -87,30 +95,27 @@ public final class SpringWebfluxTelemetryBuilder {
 
   /**
    * Returns a new {@link SpringWebfluxTelemetry} with the settings of this {@link
-   * SpringWebfluxTelemetryBuilder}.
+   * SpringWebfluxTelemetryClientBuilder}.
    */
-  public SpringWebfluxTelemetry build() {
-    SpringWebfluxHttpAttributesGetter httpAttributesGetter =
-        SpringWebfluxHttpAttributesGetter.INSTANCE;
+  public Instrumenter<ClientRequest, ClientResponse> build() {
+    WebClientHttpAttributesGetter httpClientAttributesGetter =
+        WebClientHttpAttributesGetter.INSTANCE;
 
-    InstrumenterBuilder<ClientRequest, ClientResponse> builder =
+    InstrumenterBuilder<ClientRequest, ClientResponse> clientBuilder =
         Instrumenter.<ClientRequest, ClientResponse>builder(
                 openTelemetry,
-                "io.opentelemetry.spring-webflux-5.0",
-                HttpSpanNameExtractor.create(httpAttributesGetter))
-            .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter))
-            .addAttributesExtractor(httpAttributesExtractorBuilder.build())
-            .addAttributesExtractors(additionalExtractors)
+                INSTRUMENTATION_NAME,
+                HttpSpanNameExtractor.create(httpClientAttributesGetter))
+            .setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpClientAttributesGetter))
+            .addAttributesExtractor(httpClientAttributesExtractorBuilder.build())
+            .addAttributesExtractors(clientAdditionalExtractors)
             .addOperationMetrics(HttpClientMetrics.get());
 
     if (captureExperimentalSpanAttributes) {
-      builder.addAttributesExtractor(new SpringWebfluxExperimentalAttributesExtractor());
+      clientBuilder.addAttributesExtractor(new WebClientExperimentalAttributesExtractor());
     }
 
     // headers are injected elsewhere; ClientRequest is immutable
-    Instrumenter<ClientRequest, ClientResponse> instrumenter =
-        builder.buildInstrumenter(alwaysClient());
-
-    return new SpringWebfluxTelemetry(instrumenter, openTelemetry.getPropagators());
+    return clientBuilder.buildInstrumenter(alwaysClient());
   }
 }

+ 1 - 1
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/StatusCodes.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/StatusCodes.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import static java.util.logging.Level.FINE;
 

+ 1 - 1
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/TraceWebClientSubscriber.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/TraceWebClientSubscriber.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import io.opentelemetry.context.Context;
 import io.opentelemetry.context.Scope;

+ 2 - 2
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/SpringWebfluxExperimentalAttributesExtractor.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientExperimentalAttributesExtractor.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import static io.opentelemetry.api.common.AttributeKey.stringKey;
 
@@ -15,7 +15,7 @@ import javax.annotation.Nullable;
 import org.springframework.web.reactive.function.client.ClientRequest;
 import org.springframework.web.reactive.function.client.ClientResponse;
 
-final class SpringWebfluxExperimentalAttributesExtractor
+final class WebClientExperimentalAttributesExtractor
     implements AttributesExtractor<ClientRequest, ClientResponse> {
 
   private static final AttributeKey<String> SPRING_WEBFLUX_EVENT =

+ 2 - 2
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/SpringWebfluxHttpAttributesGetter.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientHttpAttributesGetter.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import static java.util.Collections.emptyList;
 
@@ -13,7 +13,7 @@ import javax.annotation.Nullable;
 import org.springframework.web.reactive.function.client.ClientRequest;
 import org.springframework.web.reactive.function.client.ClientResponse;
 
-enum SpringWebfluxHttpAttributesGetter
+enum WebClientHttpAttributesGetter
     implements HttpClientAttributesGetter<ClientRequest, ClientResponse> {
   INSTANCE;
 

+ 2 - 2
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/internal/SpringWebfluxNetAttributesGetter.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientNetAttributesGetter.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client.internal;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter;
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
@@ -15,7 +15,7 @@ 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.
  */
-public final class SpringWebfluxNetAttributesGetter
+public final class WebClientNetAttributesGetter
     implements NetClientAttributesGetter<ClientRequest, ClientResponse> {
 
   @Override

+ 5 - 2
instrumentation/spring/spring-webflux-5.0/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/WebClientTracingFilter.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/WebClientTracingFilter.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3.internal;
 
 import io.opentelemetry.context.Context;
 import io.opentelemetry.context.Scope;
@@ -19,8 +19,11 @@ import reactor.core.publisher.Mono;
 /**
  * Based on Spring Sleuth's Reactor instrumentation.
  * https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java
+ *
+ * <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
  */
-class WebClientTracingFilter implements ExchangeFilterFunction {
+public class WebClientTracingFilter implements ExchangeFilterFunction {
 
   private final Instrumenter<ClientRequest, ClientResponse> instrumenter;
   private final ContextPropagators propagators;

+ 3 - 3
instrumentation/spring/spring-webflux-5.0/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_0/client/SpringWebfluxClientInstrumentationTest.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxClientInstrumentationTest.java

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.instrumentation.spring.webflux.v5_0.client;
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
 
 import io.opentelemetry.instrumentation.spring.webflux.client.AbstractSpringWebfluxClientInstrumentationTest;
 import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
@@ -23,9 +23,9 @@ class SpringWebfluxClientInstrumentationTest
   protected WebClient.Builder instrument(WebClient.Builder builder) {
     SpringWebfluxTelemetry instrumentation =
         SpringWebfluxTelemetry.builder(testing.getOpenTelemetry())
-            .setCapturedRequestHeaders(
+            .setCapturedClientRequestHeaders(
                 Collections.singletonList(AbstractHttpClientTest.TEST_REQUEST_HEADER))
-            .setCapturedResponseHeaders(
+            .setCapturedClientResponseHeaders(
                 Collections.singletonList(AbstractHttpClientTest.TEST_RESPONSE_HEADER))
             .build();
     return builder.filters(instrumentation::addClientTracingFilter);

+ 48 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/SpringWebfluxServerInstrumentationTest.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
+import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public final class SpringWebfluxServerInstrumentationTest
+    extends AbstractHttpServerTest<ConfigurableApplicationContext> {
+
+  private static final String CONTEXT_PATH = "/test";
+
+  @RegisterExtension
+  static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forLibrary();
+
+  @Override
+  protected ConfigurableApplicationContext setupServer() {
+    return TestWebfluxSpringBootApp.start(port, CONTEXT_PATH);
+  }
+
+  @Override
+  public void stopServer(ConfigurableApplicationContext applicationContext) {
+    applicationContext.close();
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    options.setContextPath(CONTEXT_PATH);
+    options.setTestPathParam(true);
+    options.setExpectedException(new RuntimeException(ServerEndpoint.EXCEPTION.getBody()));
+
+    options.setExpectedHttpRoute(
+        endpoint -> {
+          if (endpoint == ServerEndpoint.PATH_PARAM) {
+            return CONTEXT_PATH + "/path/{id}/param";
+          }
+          return expectedHttpRoute(endpoint);
+        });
+  }
+}

+ 133 - 0
instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/test/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/TestWebfluxSpringBootApp.java

@@ -0,0 +1,133 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webflux.v5_3;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS;
+import static java.util.Collections.singletonList;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
+import java.net.URI;
+import java.util.Properties;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.server.WebFilter;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@SpringBootApplication
+class TestWebfluxSpringBootApp {
+
+  static ConfigurableApplicationContext start(int port, String contextPath) {
+    Properties props = new Properties();
+    props.put("server.port", port);
+    props.put("spring.webflux.base-path", contextPath);
+
+    SpringApplication app = new SpringApplication(TestWebfluxSpringBootApp.class);
+    app.setDefaultProperties(props);
+    return app.run();
+  }
+
+  @Bean
+  WebFilter telemetryFilter() {
+    return SpringWebfluxTelemetry.builder(GlobalOpenTelemetry.get())
+        .setCapturedServerRequestHeaders(singletonList(AbstractHttpServerTest.TEST_REQUEST_HEADER))
+        .setCapturedServerResponseHeaders(
+            singletonList(AbstractHttpServerTest.TEST_RESPONSE_HEADER))
+        .build()
+        .createWebFilter();
+  }
+
+  @Controller
+  static class TestController {
+
+    @RequestMapping("/success")
+    @ResponseBody
+    Flux<String> success() {
+      return Flux.defer(() -> Flux.just(controller(SUCCESS, SUCCESS::getBody)));
+    }
+
+    @RequestMapping("/query")
+    @ResponseBody
+    String query_param(@RequestParam("some") String param) {
+      return controller(QUERY_PARAM, () -> "some=" + param);
+    }
+
+    @RequestMapping("/redirect")
+    @ResponseBody
+    Mono<Void> redirect(ServerHttpResponse response) {
+      response.setStatusCode(HttpStatus.FOUND);
+      response.getHeaders().setLocation(URI.create("/redirected"));
+      return controller(REDIRECT, response::setComplete);
+    }
+
+    @RequestMapping("/error-status")
+    Mono<ResponseEntity<String>> error() {
+      return Mono.just(
+          controller(
+              ERROR,
+              () -> new ResponseEntity<>(ERROR.getBody(), HttpStatus.valueOf(ERROR.getStatus()))));
+    }
+
+    @RequestMapping("/exception")
+    Flux<ResponseEntity<String>> exception() throws Exception {
+      return Flux.just(
+          controller(
+              EXCEPTION,
+              () -> {
+                throw new RuntimeException(EXCEPTION.getBody());
+              }));
+    }
+
+    @RequestMapping("/captureHeaders")
+    ResponseEntity<String> capture_headers(
+        @RequestHeader("X-Test-Request") String testRequestHeader) {
+      return controller(
+          CAPTURE_HEADERS,
+          () ->
+              ResponseEntity.ok()
+                  .header("X-Test-Response", testRequestHeader)
+                  .body(CAPTURE_HEADERS.getBody()));
+    }
+
+    @RequestMapping("/path/{id}/param")
+    @ResponseBody
+    String path_param(@PathVariable("id") int id) {
+      return controller(PATH_PARAM, () -> String.valueOf(id));
+    }
+
+    @RequestMapping("/child")
+    @ResponseBody
+    Mono<String> indexed_child(@RequestParam("id") String id) {
+      return Mono.just(
+          controller(
+              INDEXED_CHILD,
+              () -> {
+                INDEXED_CHILD.collectSpanAttributes(name -> name.equals("id") ? id : null);
+                return INDEXED_CHILD.getBody();
+              }));
+    }
+  }
+}

+ 0 - 0
instrumentation/spring/spring-webflux-5.0/testing/build.gradle.kts → instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/build.gradle.kts


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/AbstractSpringWebfluxClientInstrumentationTest.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/AbstractSpringWebfluxClientInstrumentationTest.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/ClientHttpConnectorFactory.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/ClientHttpConnectorFactory.java


+ 0 - 0
instrumentation/spring/spring-webflux-5.0/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxSingleConnection.java → instrumentation/spring/spring-webflux/spring-webflux-5.3/testing/src/main/java/io/opentelemetry/instrumentation/spring/webflux/client/SpringWebfluxSingleConnection.java


+ 3 - 3
settings.gradle.kts

@@ -473,9 +473,9 @@ hideFromDependabot(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:java
 hideFromDependabot(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library")
 hideFromDependabot(":instrumentation:spring:spring-webmvc:spring-webmvc-common:javaagent")
 hideFromDependabot(":instrumentation:spring:spring-webmvc:spring-webmvc-common:testing")
-hideFromDependabot(":instrumentation:spring:spring-webflux-5.0:javaagent")
-hideFromDependabot(":instrumentation:spring:spring-webflux-5.0:library")
-hideFromDependabot(":instrumentation:spring:spring-webflux-5.0:testing")
+hideFromDependabot(":instrumentation:spring:spring-webflux:spring-webflux-5.0:javaagent")
+hideFromDependabot(":instrumentation:spring:spring-webflux:spring-webflux-5.3:testing")
+hideFromDependabot(":instrumentation:spring:spring-webflux:spring-webflux-5.3:library")
 hideFromDependabot(":instrumentation:spring:spring-ws-2.0:javaagent")
 hideFromDependabot(":instrumentation:spring:spring-boot-autoconfigure")
 hideFromDependabot(":instrumentation:spring:starters:spring-boot-starter")

+ 5 - 3
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java

@@ -521,9 +521,11 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
           assertThat(attrs).containsEntry(SemanticAttributes.HTTP_METHOD, method);
           assertThat(attrs).containsEntry(SemanticAttributes.HTTP_STATUS_CODE, endpoint.status);
 
-          assertThat(attrs)
-              .hasEntrySatisfying(
-                  SemanticAttributes.HTTP_FLAVOR, entry -> assertThat(entry).isIn("1.1", "2.0"));
+          if (attrs.get(SemanticAttributes.HTTP_FLAVOR) != null) {
+            assertThat(attrs)
+                .hasEntrySatisfying(
+                    SemanticAttributes.HTTP_FLAVOR, entry -> assertThat(entry).isIn("1.1", "2.0"));
+          }
           assertThat(attrs).containsEntry(SemanticAttributes.HTTP_USER_AGENT, TEST_USER_AGENT);
 
           assertThat(attrs).containsEntry(SemanticAttributes.HTTP_SCHEME, "http");