Pārlūkot izejas kodu

Add http server stable semconv tests (#9360)

Lauri Tulmin 1 gadu atpakaļ
vecāks
revīzija
d20b357c3d
34 mainītis faili ar 728 papildinājumiem un 239 dzēšanām
  1. 0 5
      instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts
  2. 0 5
      instrumentation/armeria-1.3/javaagent/build.gradle.kts
  3. 0 5
      instrumentation/armeria-1.3/library/build.gradle.kts
  4. 13 5
      instrumentation/finatra-2.9/javaagent/build.gradle.kts
  5. 14 4
      instrumentation/grails-3.0/javaagent/build.gradle.kts
  6. 15 5
      instrumentation/grizzly-2.3/javaagent/build.gradle.kts
  7. 10 0
      instrumentation/jetty/jetty-11.0/javaagent/build.gradle.kts
  8. 10 0
      instrumentation/jetty/jetty-8.0/javaagent/build.gradle.kts
  9. 0 5
      instrumentation/netty/netty-3.8/javaagent/build.gradle.kts
  10. 16 2
      instrumentation/netty/netty-4.0/javaagent/build.gradle.kts
  11. 66 20
      instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy
  12. 34 10
      instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy
  13. 16 2
      instrumentation/netty/netty-4.1/javaagent/build.gradle.kts
  14. 90 28
      instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy
  15. 58 18
      instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy
  16. 0 5
      instrumentation/netty/netty-4.1/library/build.gradle.kts
  17. 8 0
      instrumentation/play/play-mvc/play-mvc-2.4/javaagent/build.gradle.kts
  18. 8 0
      instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts
  19. 14 0
      instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts
  20. 20 4
      instrumentation/ratpack/ratpack-1.7/library/build.gradle.kts
  21. 15 0
      instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts
  22. 15 5
      instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts
  23. 12 2
      instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts
  24. 12 2
      instrumentation/tomcat/tomcat-10.0/javaagent/build.gradle.kts
  25. 15 5
      instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts
  26. 10 0
      instrumentation/undertow-1.4/javaagent/build.gradle.kts
  27. 4 1
      instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java
  28. 74 34
      instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy
  29. 8 0
      instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts
  30. 10 0
      instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts
  31. 1 1
      testing-common/build.gradle.kts
  32. 1 28
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java
  33. 88 38
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java
  34. 71 0
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/SemconvStabilityUtil.java

+ 0 - 5
instrumentation/akka/akka-http-10.0/javaagent/build.gradle.kts

@@ -44,11 +44,6 @@ dependencies {
 
 tasks {
   val testStableSemconv by registering(Test::class) {
-    filter {
-      includeTestsMatching("AkkaHttpClientInstrumentationTest")
-    }
-    include("**/AkkaHttpClientInstrumentationTest.*")
-
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 

+ 0 - 5
instrumentation/armeria-1.3/javaagent/build.gradle.kts

@@ -22,11 +22,6 @@ dependencies {
 
 tasks {
   val testStableSemconv by registering(Test::class) {
-    filter {
-      includeTestsMatching("ArmeriaHttpClientTest")
-    }
-    include("**/ArmeriaHttpClientTest.*")
-
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 

+ 0 - 5
instrumentation/armeria-1.3/library/build.gradle.kts

@@ -11,11 +11,6 @@ dependencies {
 
 tasks {
   val testStableSemconv by registering(Test::class) {
-    filter {
-      includeTestsMatching("ArmeriaHttpClientTest")
-    }
-    include("**/ArmeriaHttpClientTest.*")
-
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 

+ 13 - 5
instrumentation/finatra-2.9/javaagent/build.gradle.kts

@@ -81,12 +81,20 @@ tasks {
       dependsOn(testing.suites)
     }
   }
-}
 
-tasks.withType<Test>().configureEach {
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  withType<Test>().configureEach {
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }
 
 // com.fasterxml.jackson.module:jackson-module-scala_2.11:2.15.2 is missing force using jackson 2.15.1

+ 14 - 4
instrumentation/grails-3.0/javaagent/build.gradle.kts

@@ -65,8 +65,18 @@ configurations.testRuntimeClasspath {
   }
 }
 
-tasks.withType<Test>().configureEach {
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 15 - 5
instrumentation/grizzly-2.3/javaagent/build.gradle.kts

@@ -19,12 +19,22 @@ dependencies {
   testLibrary("org.glassfish.grizzly:grizzly-http-server:2.3")
 }
 
-tasks.withType<Test>().configureEach {
-  jvmArgs("-Dotel.instrumentation.grizzly.enabled=true")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    jvmArgs("-Dotel.instrumentation.grizzly.enabled=true")
 
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }
 
 // Requires old Guava. Can't use enforcedPlatform since predates BOM

+ 10 - 0
instrumentation/jetty/jetty-11.0/javaagent/build.gradle.kts

@@ -27,3 +27,13 @@ dependencies {
 otelJava {
   minJavaVersionSupported.set(JavaVersion.VERSION_11)
 }
+
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
+}

+ 10 - 0
instrumentation/jetty/jetty-8.0/javaagent/build.gradle.kts

@@ -37,3 +37,13 @@ if (latestDepTest) {
     minJavaVersionSupported.set(JavaVersion.VERSION_11)
   }
 }
+
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
+}

+ 0 - 5
instrumentation/netty/netty-3.8/javaagent/build.gradle.kts

@@ -37,11 +37,6 @@ dependencies {
 
 tasks {
   val testStableSemconv by registering(Test::class) {
-    filter {
-      includeTestsMatching("*ClientTest")
-    }
-    include("**/*ClientTest.*")
-
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 

+ 16 - 2
instrumentation/netty/netty-4.0/javaagent/build.gradle.kts

@@ -42,16 +42,29 @@ tasks {
       includeTestsMatching("Netty40ClientSslTest")
     }
     include("**/Netty40ConnectionSpanTest.*", "**/Netty40ClientSslTest.*")
+
     jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true")
     jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true")
   }
 
   val testStableSemconv by registering(Test::class) {
     filter {
-      includeTestsMatching("*ClientTest")
+      excludeTestsMatching("Netty40ConnectionSpanTest")
+      excludeTestsMatching("Netty40ClientSslTest")
+    }
+
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  val testStableSemconvConnectionSpan by registering(Test::class) {
+    filter {
+      includeTestsMatching("Netty40ConnectionSpanTest")
+      includeTestsMatching("Netty40ClientSslTest")
     }
-    include("**/*ClientTest.*")
+    include("**/Netty40ConnectionSpanTest.*", "**/Netty40ClientSslTest.*")
 
+    jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true")
+    jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true")
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 
@@ -65,6 +78,7 @@ tasks {
   check {
     dependsOn(testConnectionSpan)
     dependsOn(testStableSemconv)
+    dependsOn(testStableSemconvConnectionSpan)
   }
 }
 

+ 66 - 20
instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ClientSslTest.groovy

@@ -18,6 +18,8 @@ import io.netty.handler.codec.http.HttpHeaders
 import io.netty.handler.codec.http.HttpMethod
 import io.netty.handler.codec.http.HttpVersion
 import io.netty.handler.ssl.SslHandler
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
+import io.opentelemetry.instrumentation.api.internal.SemconvStability
 import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
 import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
@@ -88,11 +90,22 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification {
           name "CONNECT"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(2) {
@@ -102,11 +115,22 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification {
           status ERROR
           // netty swallows the exception, it doesn't make any sense to hard-code the message
           errorEventWithAnyMessage(SSLHandshakeException)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
-            "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+              "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_SOCKET_DOMAIN" uri.host
+              "$NetworkAttributes.SERVER_SOCKET_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
       }
@@ -144,22 +168,44 @@ class Netty40ClientSslTest extends AgentInstrumentationSpecification {
           name "CONNECT"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(2) {
           name "SSL handshake"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
-            "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+              "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_SOCKET_DOMAIN" uri.host
+              "$NetworkAttributes.SERVER_SOCKET_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(3) {

+ 34 - 10
instrumentation/netty/netty-4.0/javaagent/src/test/groovy/Netty40ConnectionSpanTest.groovy

@@ -16,6 +16,8 @@ import io.netty.handler.codec.http.HttpClientCodec
 import io.netty.handler.codec.http.HttpHeaders
 import io.netty.handler.codec.http.HttpMethod
 import io.netty.handler.codec.http.HttpVersion
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
+import io.opentelemetry.instrumentation.api.internal.SemconvStability
 import io.opentelemetry.instrumentation.test.AgentTestTrait
 import io.opentelemetry.instrumentation.test.InstrumentationSpecification
 import io.opentelemetry.instrumentation.test.utils.PortUtils
@@ -104,11 +106,22 @@ class Netty40ConnectionSpanTest extends InstrumentationSpecification implements
           name "CONNECT"
           kind INTERNAL
           childOf(span(0))
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(2) {
@@ -152,11 +165,22 @@ class Netty40ConnectionSpanTest extends InstrumentationSpecification implements
           childOf(span(0))
           status ERROR
           errorEvent(thrownException.class, thrownException.message)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == null }
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" { it == "127.0.0.1" || it == null }
+            }
           }
         }
       }

+ 16 - 2
instrumentation/netty/netty-4.1/javaagent/build.gradle.kts

@@ -49,16 +49,29 @@ tasks {
       includeTestsMatching("Netty41ClientSslTest")
     }
     include("**/Netty41ConnectionSpanTest.*", "**/Netty41ClientSslTest.*")
+
     jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true")
     jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true")
   }
 
   val testStableSemconv by registering(Test::class) {
     filter {
-      includeTestsMatching("*ClientTest")
+      excludeTestsMatching("Netty41ConnectionSpanTest")
+      excludeTestsMatching("Netty41ClientSslTest")
+    }
+
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  val testStableSemconvConnectionSpan by registering(Test::class) {
+    filter {
+      includeTestsMatching("Netty41ConnectionSpanTest")
+      includeTestsMatching("Netty41ClientSslTest")
     }
-    include("**/*ClientTest.*")
+    include("**/Netty41ConnectionSpanTest.*", "**/Netty41ClientSslTest.*")
 
+    jvmArgs("-Dotel.instrumentation.netty.connection-telemetry.enabled=true")
+    jvmArgs("-Dotel.instrumentation.netty.ssl-telemetry.enabled=true")
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 
@@ -74,6 +87,7 @@ tasks {
   check {
     dependsOn(testConnectionSpan)
     dependsOn(testStableSemconv)
+    dependsOn(testStableSemconvConnectionSpan)
   }
 }
 

+ 90 - 28
instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ClientSslTest.groovy

@@ -20,6 +20,8 @@ import io.netty.handler.codec.http.HttpVersion
 import io.netty.handler.ssl.SslContext
 import io.netty.handler.ssl.SslContextBuilder
 import io.netty.handler.ssl.SslHandler
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
+import io.opentelemetry.instrumentation.api.internal.SemconvStability
 import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler
 import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
 import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer
@@ -93,21 +95,40 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification {
           name "RESOLVE"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+            }
           }
         }
         span(2) {
           name "CONNECT"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(3) {
@@ -117,11 +138,22 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification {
           status ERROR
           // netty swallows the exception, it doesn't make any sense to hard-code the message
           errorEventWithAnyMessage(SSLHandshakeException)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
-            "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+              "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_SOCKET_DOMAIN" uri.host
+              "$NetworkAttributes.SERVER_SOCKET_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
       }
@@ -161,32 +193,62 @@ class Netty41ClientSslTest extends AgentInstrumentationSpecification {
           name "RESOLVE"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+            }
           }
         }
         span(2) {
           name "CONNECT"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(3) {
           name "SSL handshake"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
-            "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+              "$SemanticAttributes.NET_SOCK_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_SOCKET_DOMAIN" uri.host
+              "$NetworkAttributes.SERVER_SOCKET_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(4) {

+ 58 - 18
instrumentation/netty/netty-4.1/javaagent/src/test/groovy/Netty41ConnectionSpanTest.groovy

@@ -16,6 +16,8 @@ import io.netty.handler.codec.http.HttpClientCodec
 import io.netty.handler.codec.http.HttpHeaderNames
 import io.netty.handler.codec.http.HttpMethod
 import io.netty.handler.codec.http.HttpVersion
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
+import io.opentelemetry.instrumentation.api.internal.SemconvStability
 import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler
 import io.opentelemetry.instrumentation.test.AgentTestTrait
 import io.opentelemetry.instrumentation.test.InstrumentationSpecification
@@ -107,21 +109,40 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
           name "RESOLVE"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+            }
           }
         }
         span(2) {
           name "CONNECT"
           kind INTERNAL
           childOf(span(0))
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
+            }
           }
         }
         span(3) {
@@ -165,10 +186,18 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
           name "RESOLVE"
           kind INTERNAL
           childOf span(0)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+            }
           }
         }
         span(2) {
@@ -177,11 +206,22 @@ class Netty41ConnectionSpanTest extends InstrumentationSpecification implements
           childOf span(0)
           status ERROR
           errorEvent(thrownException.class, thrownException.message)
-          attributes {
-            "$SemanticAttributes.NET_TRANSPORT" IP_TCP
-            "$SemanticAttributes.NET_PEER_NAME" uri.host
-            "$SemanticAttributes.NET_PEER_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.NET_TRANSPORT" IP_TCP
+              "$SemanticAttributes.NET_PEER_NAME" uri.host
+              "$SemanticAttributes.NET_PEER_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
+              "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == null }
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.SERVER_SOCKET_ADDRESS" { it == "127.0.0.1" || it == null }
+            }
           }
         }
       }

+ 0 - 5
instrumentation/netty/netty-4.1/library/build.gradle.kts

@@ -15,11 +15,6 @@ dependencies {
 
 tasks {
   val testStableSemconv by registering(Test::class) {
-    filter {
-      includeTestsMatching("*ClientTest")
-    }
-    include("**/*ClientTest.*")
-
     jvmArgs("-Dotel.semconv-stability.opt-in=http")
   }
 

+ 8 - 0
instrumentation/play/play-mvc/play-mvc-2.4/javaagent/build.gradle.kts

@@ -64,6 +64,14 @@ tasks {
   check {
     dependsOn(testing.suites)
   }
+
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }
 
 // play-test depends on websocket-client

+ 8 - 0
instrumentation/play/play-mvc/play-mvc-2.6/javaagent/build.gradle.kts

@@ -74,6 +74,14 @@ tasks {
       dependsOn(testing.suites)
     }
   }
+
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }
 
 // play-test depends on websocket-client

+ 14 - 0
instrumentation/ratpack/ratpack-1.4/javaagent/build.gradle.kts

@@ -45,3 +45,17 @@ if (!(findProperty("testLatestDeps") as Boolean)) {
     }
   }
 }
+
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    filter {
+      excludeTestsMatching("RatpackRoutesTest")
+    }
+
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
+}

+ 20 - 4
instrumentation/ratpack/ratpack-1.7/library/build.gradle.kts

@@ -15,8 +15,24 @@ dependencies {
   }
 }
 
-tasks.withType<Test>().configureEach {
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    filter {
+      excludeTestsMatching("InstrumentedHttpClientTest")
+      excludeTestsMatching("RatpackRoutesTest")
+      excludeTestsMatching("RatpackServerApplicationTest")
+      excludeTestsMatching("RatpackServerTest")
+    }
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 15 - 0
instrumentation/servlet/servlet-2.2/javaagent/build.gradle.kts

@@ -32,3 +32,18 @@ dependencies {
   latestDepTestLibrary("org.eclipse.jetty:jetty-server:7.+") // see servlet-3.0 module
   latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:7.+") // see servlet-3.0 module
 }
+
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    filter {
+      includeTestsMatching("JettyServlet2Test")
+    }
+    include("**/JettyServlet2Test.*")
+
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
+}

+ 15 - 5
instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts

@@ -37,9 +37,19 @@ dependencies {
   latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:9.+") // see servlet-5.0 module
 }
 
-tasks.withType<Test>().configureEach {
-  jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 12 - 2
instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts

@@ -30,6 +30,16 @@ dependencies {
   latestDepTestLibrary("org.eclipse.jetty:jetty-server:11.+")
 }
 
-tasks.withType<Test>().configureEach {
-  jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 12 - 2
instrumentation/tomcat/tomcat-10.0/javaagent/build.gradle.kts

@@ -23,6 +23,16 @@ dependencies {
   testInstrumentation(project(":instrumentation:tomcat:tomcat-7.0:javaagent"))
 }
 
-tasks.withType<Test>().configureEach {
-  jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 15 - 5
instrumentation/tomcat/tomcat-7.0/javaagent/build.gradle.kts

@@ -30,9 +30,19 @@ dependencies {
   latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:9.+") // see tomcat-10.0 module
 }
 
-tasks.withType<Test>().configureEach {
-  jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
-  // required on jdk17
-  jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
-  jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  withType<Test>().configureEach {
+    jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
+    // required on jdk17
+    jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
+    jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
 }

+ 10 - 0
instrumentation/undertow-1.4/javaagent/build.gradle.kts

@@ -20,3 +20,13 @@ dependencies {
   bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
   bootstrap(project(":instrumentation:undertow-1.4:bootstrap"))
 }
+
+tasks {
+  val testStableSemconv by registering(Test::class) {
+    jvmArgs("-Dotel.semconv-stability.opt-in=http")
+  }
+
+  check {
+    dependsOn(testStableSemconv)
+  }
+}

+ 4 - 1
instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java

@@ -55,7 +55,10 @@ public class UndertowHttpAttributesGetter
   @Nullable
   @Override
   public String getUrlQuery(HttpServerExchange exchange) {
-    return exchange.getQueryString();
+    String queryString = exchange.getQueryString();
+    // getQueryString returns empty string when query string is missing, we'll return null from
+    // here instead to void adding empty query string attribute to the span
+    return !"".equals(queryString) ? queryString : null;
   }
 
   @Nullable

+ 74 - 34
instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy

@@ -7,6 +7,10 @@ import io.opentelemetry.api.common.AttributeKey
 import io.opentelemetry.api.trace.Span
 import io.opentelemetry.api.trace.SpanKind
 import io.opentelemetry.api.trace.StatusCode
+import io.opentelemetry.instrumentation.api.instrumenter.http.internal.HttpAttributes
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
+import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes
+import io.opentelemetry.instrumentation.api.internal.SemconvStability
 import io.opentelemetry.instrumentation.test.AgentTestTrait
 import io.opentelemetry.instrumentation.test.base.HttpServerTest
 import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
@@ -142,23 +146,41 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
             eventName "after-event"
           }
 
-          attributes {
-            "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP
-            "$SemanticAttributes.HTTP_SCHEME" uri.getScheme()
-            "$SemanticAttributes.HTTP_TARGET" uri.getPath()
-            "$SemanticAttributes.HTTP_METHOD" "GET"
-            "$SemanticAttributes.HTTP_STATUS_CODE" 200
-            "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
-            "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
-            "$SemanticAttributes.HTTP_SCHEME" "http"
-            "$SemanticAttributes.HTTP_TARGET" "/sendResponse"
-            "$SemanticAttributes.NET_PROTOCOL_NAME" "http"
-            "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1"
-            "$SemanticAttributes.NET_HOST_NAME" uri.host
-            "$SemanticAttributes.NET_HOST_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" Long
-            "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1"
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP
+              "$SemanticAttributes.HTTP_SCHEME" uri.getScheme()
+              "$SemanticAttributes.HTTP_TARGET" uri.getPath()
+              "$SemanticAttributes.HTTP_METHOD" "GET"
+              "$SemanticAttributes.HTTP_STATUS_CODE" 200
+              "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
+              "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
+              "$SemanticAttributes.NET_PROTOCOL_NAME" "http"
+              "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1"
+              "$SemanticAttributes.NET_HOST_NAME" uri.host
+              "$SemanticAttributes.NET_HOST_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" Long
+              "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1"
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.CLIENT_ADDRESS" TEST_CLIENT_IP
+              "$UrlAttributes.URL_SCHEME" uri.getScheme()
+              "$UrlAttributes.URL_PATH" uri.getPath()
+              "$HttpAttributes.HTTP_REQUEST_METHOD" "GET"
+              "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200
+              "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
+              "$HttpAttributes.HTTP_RESPONSE_BODY_SIZE" Long
+              "$NetworkAttributes.NETWORK_PROTOCOL_NAME" "http"
+              "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.CLIENT_SOCKET_ADDRESS" "127.0.0.1"
+              "$NetworkAttributes.CLIENT_SOCKET_PORT" Long
+            }
           }
         }
         span(1) {
@@ -196,23 +218,41 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
           }
           errorEvent(Exception, "exception after sending response", 2)
 
-          attributes {
-            "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP
-            "$SemanticAttributes.HTTP_SCHEME" uri.getScheme()
-            "$SemanticAttributes.HTTP_TARGET" uri.getPath()
-            "$SemanticAttributes.HTTP_METHOD" "GET"
-            "$SemanticAttributes.HTTP_STATUS_CODE" 200
-            "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
-            "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
-            "$SemanticAttributes.HTTP_SCHEME" "http"
-            "$SemanticAttributes.HTTP_TARGET" "/sendResponseWithException"
-            "$SemanticAttributes.NET_PROTOCOL_NAME" "http"
-            "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1"
-            "$SemanticAttributes.NET_HOST_NAME" uri.host
-            "$SemanticAttributes.NET_HOST_PORT" uri.port
-            "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
-            "$SemanticAttributes.NET_SOCK_PEER_PORT" Long
-            "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1"
+          if (SemconvStability.emitOldHttpSemconv()) {
+            attributes {
+              "$SemanticAttributes.HTTP_CLIENT_IP" TEST_CLIENT_IP
+              "$SemanticAttributes.HTTP_SCHEME" uri.getScheme()
+              "$SemanticAttributes.HTTP_TARGET" uri.getPath()
+              "$SemanticAttributes.HTTP_METHOD" "GET"
+              "$SemanticAttributes.HTTP_STATUS_CODE" 200
+              "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
+              "$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
+              "$SemanticAttributes.NET_PROTOCOL_NAME" "http"
+              "$SemanticAttributes.NET_PROTOCOL_VERSION" "1.1"
+              "$SemanticAttributes.NET_HOST_NAME" uri.host
+              "$SemanticAttributes.NET_HOST_PORT" uri.port
+              "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
+              "$SemanticAttributes.NET_SOCK_PEER_PORT" Long
+              "$SemanticAttributes.NET_SOCK_HOST_ADDR" "127.0.0.1"
+            }
+          }
+          if (SemconvStability.emitStableHttpSemconv()) {
+            attributes {
+              "$NetworkAttributes.CLIENT_ADDRESS" TEST_CLIENT_IP
+              "$UrlAttributes.URL_SCHEME" uri.getScheme()
+              "$UrlAttributes.URL_PATH" uri.getPath()
+              "$HttpAttributes.HTTP_REQUEST_METHOD" "GET"
+              "$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200
+              "$SemanticAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
+              "$HttpAttributes.HTTP_RESPONSE_BODY_SIZE" Long
+              "$NetworkAttributes.NETWORK_PROTOCOL_NAME" "http"
+              "$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1"
+              "$NetworkAttributes.NETWORK_TYPE" "ipv4"
+              "$NetworkAttributes.SERVER_ADDRESS" uri.host
+              "$NetworkAttributes.SERVER_PORT" uri.port
+              "$NetworkAttributes.CLIENT_SOCKET_ADDRESS" "127.0.0.1"
+              "$NetworkAttributes.CLIENT_SOCKET_PORT" Long
+            }
           }
         }
         span(1) {

+ 8 - 0
instrumentation/vertx/vertx-rx-java-3.5/javaagent/build.gradle.kts

@@ -67,5 +67,13 @@ tasks {
     check {
       dependsOn(testing.suites)
     }
+
+    val testStableSemconv by registering(Test::class) {
+      jvmArgs("-Dotel.semconv-stability.opt-in=http")
+    }
+
+    check {
+      dependsOn(testStableSemconv)
+    }
   }
 }

+ 10 - 0
instrumentation/vertx/vertx-web-3.0/javaagent/build.gradle.kts

@@ -59,4 +59,14 @@ tasks {
       dependsOn(testing.suites)
     }
   }
+
+  tasks {
+    val testStableSemconv by registering(Test::class) {
+      jvmArgs("-Dotel.semconv-stability.opt-in=http")
+    }
+
+    check {
+      dependsOn(testStableSemconv)
+    }
+  }
 }

+ 1 - 1
testing-common/build.gradle.kts

@@ -59,7 +59,7 @@ dependencies {
   implementation("org.slf4j:jcl-over-slf4j")
   implementation("org.slf4j:jul-to-slf4j")
   implementation("io.opentelemetry:opentelemetry-exporter-logging")
-  implementation(project(":instrumentation-api-semconv"))
+  api(project(":instrumentation-api-semconv"))
 
   annotationProcessor("com.google.auto.service:auto-service")
   compileOnly("com.google.auto.service:auto-service")

+ 1 - 28
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpClientTest.java

@@ -19,7 +19,6 @@ import io.opentelemetry.api.trace.Span;
 import io.opentelemetry.api.trace.SpanKind;
 import io.opentelemetry.instrumentation.api.instrumenter.http.internal.HttpAttributes;
 import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
-import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
 import io.opentelemetry.instrumentation.api.internal.HttpConstants;
 import io.opentelemetry.instrumentation.api.internal.SemconvStability;
 import io.opentelemetry.instrumentation.test.utils.PortUtils;
@@ -1081,34 +1080,8 @@ public abstract class AbstractHttpClientTest<REQUEST> implements HttpClientTypeA
             });
   }
 
-  @SuppressWarnings("unchecked")
   protected static <T> AttributeKey<T> getAttributeKey(AttributeKey<T> oldKey) {
-    if (SemconvStability.emitStableHttpSemconv()) {
-      if (SemanticAttributes.NET_PROTOCOL_NAME == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.NETWORK_PROTOCOL_NAME;
-      } else if (SemanticAttributes.NET_PROTOCOL_VERSION == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.NETWORK_PROTOCOL_VERSION;
-      } else if (SemanticAttributes.NET_PEER_NAME == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.SERVER_ADDRESS;
-      } else if (SemanticAttributes.NET_PEER_PORT == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.SERVER_PORT;
-      } else if (SemanticAttributes.NET_SOCK_PEER_ADDR == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.CLIENT_SOCKET_ADDRESS;
-      } else if (SemanticAttributes.NET_SOCK_PEER_PORT == oldKey) {
-        return (AttributeKey<T>) NetworkAttributes.CLIENT_SOCKET_PORT;
-      } else if (SemanticAttributes.HTTP_URL == oldKey) {
-        return (AttributeKey<T>) UrlAttributes.URL_FULL;
-      } else if (SemanticAttributes.HTTP_METHOD == oldKey) {
-        return (AttributeKey<T>) HttpAttributes.HTTP_REQUEST_METHOD;
-      } else if (SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH == oldKey) {
-        return (AttributeKey<T>) HttpAttributes.HTTP_REQUEST_BODY_SIZE;
-      } else if (SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH == oldKey) {
-        return (AttributeKey<T>) HttpAttributes.HTTP_RESPONSE_BODY_SIZE;
-      } else if (SemanticAttributes.HTTP_STATUS_CODE == oldKey) {
-        return (AttributeKey<T>) HttpAttributes.HTTP_RESPONSE_STATUS_CODE;
-      }
-    }
-    return oldKey;
+    return SemconvStabilityUtil.getAttributeKey(oldKey);
   }
 
   private static Set<AttributeKey<?>> getAttributeKeys(Set<AttributeKey<?>> oldKeys) {

+ 88 - 38
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java

@@ -16,6 +16,7 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
 import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS;
 import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
 import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
+import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -26,6 +27,9 @@ import io.opentelemetry.api.trace.SpanKind;
 import io.opentelemetry.context.Context;
 import io.opentelemetry.context.propagation.TextMapPropagator;
 import io.opentelemetry.context.propagation.TextMapSetter;
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
+import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
+import io.opentelemetry.instrumentation.api.internal.SemconvStability;
 import io.opentelemetry.instrumentation.testing.GlobalTraceUtil;
 import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
 import io.opentelemetry.sdk.testing.assertj.TraceAssert;
@@ -645,78 +649,113 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
 
     span.hasAttributesSatisfying(
         attrs -> {
-          if (httpAttributes.contains(SemanticAttributes.NET_TRANSPORT)) {
-            assertThat(attrs)
-                .containsEntry(
-                    SemanticAttributes.NET_TRANSPORT, SemanticAttributes.NetTransportValues.IP_TCP);
+          if (SemconvStability.emitOldHttpSemconv()
+              && attrs.get(SemanticAttributes.NET_TRANSPORT) != null) {
+            assertThat(attrs).containsEntry(SemanticAttributes.NET_TRANSPORT, IP_TCP);
+          }
+          if (SemconvStability.emitStableHttpSemconv()
+              && attrs.get(NetworkAttributes.NETWORK_TRANSPORT) != null) {
+            assertThat(attrs).containsEntry(NetworkAttributes.NETWORK_TRANSPORT, "tcp");
+          }
+          if (SemconvStability.emitStableHttpSemconv()
+              && attrs.get(NetworkAttributes.NETWORK_TYPE) != null) {
+            assertThat(attrs).containsEntry(NetworkAttributes.NETWORK_TYPE, "ipv4");
           }
 
-          assertThat(attrs).containsEntry(SemanticAttributes.NET_HOST_NAME, "localhost");
+          assertThat(attrs)
+              .containsEntry(getAttributeKey(SemanticAttributes.NET_HOST_NAME), "localhost");
           // TODO: Move to test knob rather than always treating as optional
           // TODO: once httpAttributes test knob is used, verify default port values
-          if (attrs.get(SemanticAttributes.NET_HOST_PORT) != null) {
-            assertThat(attrs).containsEntry(SemanticAttributes.NET_HOST_PORT, port);
+          AttributeKey<Long> netHostPortKey = getAttributeKey(SemanticAttributes.NET_HOST_PORT);
+          if (attrs.get(netHostPortKey) != null) {
+            assertThat(attrs).containsEntry(netHostPortKey, port);
           }
-          if (attrs.get(SemanticAttributes.NET_SOCK_PEER_PORT) != null) {
+          AttributeKey<Long> netSockPeerPortKey =
+              getAttributeKey(SemanticAttributes.NET_SOCK_PEER_PORT);
+          if (attrs.get(netSockPeerPortKey) != null) {
             assertThat(attrs)
                 .hasEntrySatisfying(
-                    SemanticAttributes.NET_SOCK_PEER_PORT,
+                    netSockPeerPortKey,
                     value ->
                         assertThat(value)
                             .isInstanceOf(Long.class)
                             .isNotEqualTo(Long.valueOf(port)));
           }
-          if (attrs.get(SemanticAttributes.NET_SOCK_PEER_ADDR) != null) {
+          AttributeKey<String> netSockPeerAddrKey =
+              getAttributeKey(SemanticAttributes.NET_SOCK_PEER_ADDR);
+          if (attrs.get(netSockPeerAddrKey) != null) {
             assertThat(attrs)
-                .containsEntry(
-                    SemanticAttributes.NET_SOCK_PEER_ADDR, options.sockPeerAddr.apply(endpoint));
+                .containsEntry(netSockPeerAddrKey, options.sockPeerAddr.apply(endpoint));
           }
-          if (attrs.get(SemanticAttributes.NET_SOCK_HOST_ADDR) != null) {
-            assertThat(attrs).containsEntry(SemanticAttributes.NET_SOCK_HOST_ADDR, "127.0.0.1");
+          AttributeKey<String> netSockHostAddrKey =
+              getAttributeKey(SemanticAttributes.NET_SOCK_PEER_ADDR);
+          if (attrs.get(netSockHostAddrKey) != null) {
+            assertThat(attrs).containsEntry(netSockHostAddrKey, "127.0.0.1");
           }
 
           assertThat(attrs)
               .hasEntrySatisfying(
-                  SemanticAttributes.HTTP_CLIENT_IP,
+                  getAttributeKey(SemanticAttributes.HTTP_CLIENT_IP),
                   entry ->
                       assertThat(entry)
                           .satisfiesAnyOf(
                               value -> assertThat(value).isNull(),
                               value -> assertThat(value).isEqualTo(TEST_CLIENT_IP)));
-          assertThat(attrs).containsEntry(SemanticAttributes.HTTP_METHOD, method);
-          assertThat(attrs).containsEntry(SemanticAttributes.HTTP_STATUS_CODE, endpoint.status);
+          if (SemconvStability.emitStableHttpSemconv()
+              && attrs.get(NetworkAttributes.CLIENT_PORT) != null) {
+            assertThat(attrs)
+                .hasEntrySatisfying(
+                    NetworkAttributes.CLIENT_PORT, port -> assertThat(port).isGreaterThan(0));
+          }
+          assertThat(attrs).containsEntry(getAttributeKey(SemanticAttributes.HTTP_METHOD), method);
+          assertThat(attrs)
+              .containsEntry(getAttributeKey(SemanticAttributes.HTTP_STATUS_CODE), endpoint.status);
 
-          if (attrs.get(SemanticAttributes.NET_PROTOCOL_NAME) != null) {
-            assertThat(attrs).containsEntry(SemanticAttributes.NET_PROTOCOL_NAME, "http");
+          AttributeKey<String> netProtocolKey =
+              getAttributeKey(SemanticAttributes.NET_PROTOCOL_NAME);
+          if (attrs.get(netProtocolKey) != null) {
+            assertThat(attrs).containsEntry(netProtocolKey, "http");
           }
-          if (attrs.get(SemanticAttributes.NET_PROTOCOL_VERSION) != null) {
+          AttributeKey<String> netProtocolVersionKey =
+              getAttributeKey(SemanticAttributes.NET_PROTOCOL_VERSION);
+          if (attrs.get(netProtocolVersionKey) != null) {
             assertThat(attrs)
                 .hasEntrySatisfying(
-                    SemanticAttributes.NET_PROTOCOL_VERSION,
-                    entry -> assertThat(entry).isIn("1.1", "2.0"));
+                    netProtocolVersionKey, entry -> assertThat(entry).isIn("1.1", "2.0"));
           }
           assertThat(attrs).containsEntry(SemanticAttributes.USER_AGENT_ORIGINAL, TEST_USER_AGENT);
 
-          assertThat(attrs).containsEntry(SemanticAttributes.HTTP_SCHEME, "http");
+          assertThat(attrs).containsEntry(getAttributeKey(SemanticAttributes.HTTP_SCHEME), "http");
           if (endpoint != INDEXED_CHILD) {
-            assertThat(attrs)
-                .containsEntry(
-                    SemanticAttributes.HTTP_TARGET,
-                    endpoint.resolvePath(address).getPath()
-                        + (endpoint.getQuery() != null ? "?" + endpoint.getQuery() : ""));
+            if (SemconvStability.emitOldHttpSemconv()) {
+              assertThat(attrs)
+                  .containsEntry(
+                      SemanticAttributes.HTTP_TARGET,
+                      endpoint.resolvePath(address).getPath()
+                          + (endpoint.getQuery() != null ? "?" + endpoint.getQuery() : ""));
+            }
+            if (SemconvStability.emitStableHttpSemconv()) {
+              assertThat(attrs)
+                  .containsEntry(UrlAttributes.URL_PATH, endpoint.resolvePath(address).getPath());
+              if (endpoint.getQuery() != null) {
+                assertThat(attrs).containsEntry(UrlAttributes.URL_QUERY, endpoint.getQuery());
+              }
+            }
           }
 
-          if (attrs.get(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH) != null) {
+          AttributeKey<Long> httpRequestLengthKey =
+              getAttributeKey(SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH);
+          if (attrs.get(httpRequestLengthKey) != null) {
             assertThat(attrs)
                 .hasEntrySatisfying(
-                    SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH,
-                    entry -> assertThat(entry).isNotNegative());
+                    httpRequestLengthKey, entry -> assertThat(entry).isNotNegative());
           }
-          if (attrs.get(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH) != null) {
+          AttributeKey<Long> httpResponseLengthKey =
+              getAttributeKey(SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH);
+          if (attrs.get(httpResponseLengthKey) != null) {
             assertThat(attrs)
                 .hasEntrySatisfying(
-                    SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
-                    entry -> assertThat(entry).isNotNegative());
+                    httpResponseLengthKey, entry -> assertThat(entry).isNotNegative());
           }
           if (httpAttributes.contains(SemanticAttributes.HTTP_ROUTE) && expectedRoute != null) {
             assertThat(attrs).containsEntry(SemanticAttributes.HTTP_ROUTE, expectedRoute);
@@ -738,6 +777,10 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
     return span;
   }
 
+  protected static <T> AttributeKey<T> getAttributeKey(AttributeKey<T> oldKey) {
+    return SemconvStabilityUtil.getAttributeKey(oldKey);
+  }
+
   private String getString(String method, ServerEndpoint endpoint, String expectedRoute) {
     String name = options.expectedServerSpanNameMapper.apply(endpoint, method, expectedRoute);
     return name;
@@ -749,10 +792,17 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
     String method = "GET";
     assertServerSpan(span, method, endpoint);
 
-    span.hasAttributesSatisfying(
-        equalTo(
-            SemanticAttributes.HTTP_TARGET,
-            endpoint.resolvePath(address).getPath() + "?id=" + requestId));
+    if (SemconvStability.emitOldHttpSemconv()) {
+      span.hasAttributesSatisfying(
+          equalTo(
+              SemanticAttributes.HTTP_TARGET,
+              endpoint.resolvePath(address).getPath() + "?id=" + requestId));
+    }
+    if (SemconvStability.emitStableHttpSemconv()) {
+      span.hasAttributesSatisfying(
+          equalTo(UrlAttributes.URL_PATH, endpoint.resolvePath(address).getPath()));
+      span.hasAttributesSatisfying(equalTo(UrlAttributes.URL_QUERY, "id=" + requestId));
+    }
 
     return span;
   }

+ 71 - 0
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/SemconvStabilityUtil.java

@@ -0,0 +1,71 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.testing.junit.http;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.instrumentation.api.instrumenter.http.internal.HttpAttributes;
+import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
+import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
+import io.opentelemetry.instrumentation.api.internal.SemconvStability;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+import java.util.HashMap;
+import java.util.Map;
+
+class SemconvStabilityUtil {
+  private static final Map<AttributeKey<?>, AttributeKey<?>> oldToNewMap = new HashMap<>();
+
+  static {
+    addKey(
+        oldToNewMap, SemanticAttributes.NET_PROTOCOL_NAME, NetworkAttributes.NETWORK_PROTOCOL_NAME);
+    addKey(
+        oldToNewMap,
+        SemanticAttributes.NET_PROTOCOL_VERSION,
+        NetworkAttributes.NETWORK_PROTOCOL_VERSION);
+    addKey(oldToNewMap, SemanticAttributes.NET_PEER_NAME, NetworkAttributes.SERVER_ADDRESS);
+    addKey(oldToNewMap, SemanticAttributes.NET_PEER_PORT, NetworkAttributes.SERVER_PORT);
+    addKey(
+        oldToNewMap,
+        SemanticAttributes.NET_SOCK_PEER_ADDR,
+        NetworkAttributes.CLIENT_SOCKET_ADDRESS);
+    addKey(
+        oldToNewMap, SemanticAttributes.NET_SOCK_PEER_PORT, NetworkAttributes.CLIENT_SOCKET_PORT);
+    addKey(oldToNewMap, SemanticAttributes.HTTP_URL, UrlAttributes.URL_FULL);
+    addKey(oldToNewMap, SemanticAttributes.HTTP_METHOD, HttpAttributes.HTTP_REQUEST_METHOD);
+    addKey(
+        oldToNewMap,
+        SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH,
+        HttpAttributes.HTTP_REQUEST_BODY_SIZE);
+    addKey(
+        oldToNewMap,
+        SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
+        HttpAttributes.HTTP_RESPONSE_BODY_SIZE);
+    addKey(
+        oldToNewMap, SemanticAttributes.HTTP_STATUS_CODE, HttpAttributes.HTTP_RESPONSE_STATUS_CODE);
+    addKey(oldToNewMap, SemanticAttributes.NET_HOST_NAME, NetworkAttributes.SERVER_ADDRESS);
+    addKey(oldToNewMap, SemanticAttributes.NET_HOST_PORT, NetworkAttributes.SERVER_PORT);
+    addKey(oldToNewMap, SemanticAttributes.HTTP_CLIENT_IP, NetworkAttributes.CLIENT_ADDRESS);
+    addKey(oldToNewMap, SemanticAttributes.HTTP_SCHEME, UrlAttributes.URL_SCHEME);
+    addKey(
+        oldToNewMap,
+        SemanticAttributes.NET_SOCK_HOST_ADDR,
+        NetworkAttributes.SERVER_SOCKET_ADDRESS);
+  }
+
+  private SemconvStabilityUtil() {}
+
+  private static <T> void addKey(
+      Map<AttributeKey<?>, AttributeKey<?>> map, AttributeKey<T> oldKey, AttributeKey<T> newKey) {
+    map.put(oldKey, newKey);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> AttributeKey<T> getAttributeKey(AttributeKey<T> oldKey) {
+    if (SemconvStability.emitStableHttpSemconv()) {
+      return (AttributeKey<T>) oldToNewMap.getOrDefault(oldKey, oldKey);
+    }
+    return oldKey;
+  }
+}