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); + } + } +}