diff --git a/src/OpenTelemetry.Api/Internal/HttpSemanticConventionHelper.cs b/src/OpenTelemetry.Api/Internal/HttpSemanticConventionHelper.cs
new file mode 100644
index 00000000000..1774cbd2069
--- /dev/null
+++ b/src/OpenTelemetry.Api/Internal/HttpSemanticConventionHelper.cs
@@ -0,0 +1,66 @@
+//
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+namespace OpenTelemetry.Internal;
+
+///
+/// Helper class for Http Semantic Conventions.
+///
+///
+/// Due to a breaking change in the semantic convention, affected instrumentation libraries
+/// must inspect an environment variable to determine which attributes to emit.
+/// This is expected to be removed when the instrumentation libraries reach Stable.
+/// .
+///
+internal static class HttpSemanticConventionHelper
+{
+ [Flags]
+ internal enum HttpSemanticConvention
+ {
+ ///
+ /// Instructs an instrumentation library to emit the old experimental HTTP attributes.
+ ///
+ Old = 0x1,
+
+ ///
+ /// Instructs an instrumentation library to emit the new, stable Http attributes.
+ ///
+ New = 0x2,
+
+ ///
+ /// Instructs an instrumentation library to emit both the old and new attributes.
+ ///
+ Dupe = Old | New,
+ }
+
+ public static HttpSemanticConvention GetSemanticConventionOptIn()
+ {
+ try
+ {
+ var envVarValue = Environment.GetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN");
+ return envVarValue?.ToLowerInvariant() switch
+ {
+ "http" => HttpSemanticConvention.New,
+ "http/dup" => HttpSemanticConvention.Dupe,
+ _ => HttpSemanticConvention.Old,
+ };
+ }
+ catch
+ {
+ return HttpSemanticConvention.Old;
+ }
+ }
+}
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
index 66321c1cb14..537ff5ea42e 100644
--- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
+++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
@@ -29,6 +29,7 @@
#endif
using OpenTelemetry.Internal;
using OpenTelemetry.Trace;
+using static OpenTelemetry.Internal.HttpSemanticConventionHelper;
namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
{
@@ -62,6 +63,7 @@ internal class HttpInListener : ListenerHandler
#endif
private readonly PropertyFetcher stopExceptionFetcher = new("Exception");
private readonly AspNetCoreInstrumentationOptions options;
+ private readonly HttpSemanticConvention httpSemanticConvention;
public HttpInListener(AspNetCoreInstrumentationOptions options)
: base(DiagnosticSourceName)
@@ -69,6 +71,8 @@ public HttpInListener(AspNetCoreInstrumentationOptions options)
Guard.ThrowIfNull(options);
this.options = options;
+
+ this.httpSemanticConvention = GetSemanticConventionOptIn();
}
public override void OnEventWritten(string name, object payload)
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj
index 8cea09bc65e..cc04eed9bd2 100644
--- a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj
+++ b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj
@@ -14,6 +14,7 @@
+
diff --git a/test/OpenTelemetry.Api.Tests/Internal/HttpSemanticConventionHelperTest.cs b/test/OpenTelemetry.Api.Tests/Internal/HttpSemanticConventionHelperTest.cs
new file mode 100644
index 00000000000..48a92230ab7
--- /dev/null
+++ b/test/OpenTelemetry.Api.Tests/Internal/HttpSemanticConventionHelperTest.cs
@@ -0,0 +1,67 @@
+//
+// Copyright The OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+using Xunit;
+using static OpenTelemetry.Internal.HttpSemanticConventionHelper;
+
+namespace OpenTelemetry.Api.Tests.Internal;
+
+public class HttpSemanticConventionHelperTest
+{
+ [Fact]
+ public void VerifyFlags()
+ {
+ var testValue = HttpSemanticConvention.Dupe;
+ Assert.True(testValue.HasFlag(HttpSemanticConvention.Old));
+ Assert.True(testValue.HasFlag(HttpSemanticConvention.New));
+
+ testValue = HttpSemanticConvention.Old;
+ Assert.True(testValue.HasFlag(HttpSemanticConvention.Old));
+ Assert.False(testValue.HasFlag(HttpSemanticConvention.New));
+
+ testValue = HttpSemanticConvention.New;
+ Assert.False(testValue.HasFlag(HttpSemanticConvention.Old));
+ Assert.True(testValue.HasFlag(HttpSemanticConvention.New));
+ }
+
+ [Fact]
+ public void VerifyGetSemanticConventionOptIn()
+ {
+ this.RunTestWithEnvironmentVariable(null, HttpSemanticConvention.Old);
+ this.RunTestWithEnvironmentVariable(string.Empty, HttpSemanticConvention.Old);
+ this.RunTestWithEnvironmentVariable("junk", HttpSemanticConvention.Old);
+ this.RunTestWithEnvironmentVariable("none", HttpSemanticConvention.Old);
+ this.RunTestWithEnvironmentVariable("NONE", HttpSemanticConvention.Old);
+ this.RunTestWithEnvironmentVariable("http", HttpSemanticConvention.New);
+ this.RunTestWithEnvironmentVariable("HTTP", HttpSemanticConvention.New);
+ this.RunTestWithEnvironmentVariable("http/dup", HttpSemanticConvention.Dupe);
+ this.RunTestWithEnvironmentVariable("HTTP/DUP", HttpSemanticConvention.Dupe);
+ }
+
+ private void RunTestWithEnvironmentVariable(string value, HttpSemanticConvention expected)
+ {
+ try
+ {
+ Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", value);
+
+ Assert.Equal(expected, GetSemanticConventionOptIn());
+ }
+ finally
+ {
+ Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null);
+ }
+ }
+}