diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index c87f1b4b31f..7c4c03093af 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +* **Breaking change** In order to make `RuntimeContext` compatible with + ahead-of-time compilation (AOT), + `RuntimeContext.ContextSlotType` can only be assigned one + of the following types: `AsyncLocalRuntimeContextSlot<>`, + `ThreadLocalRuntimeContextSlot<>`, and `RemotingRuntimeContextSlot<>`. A + `System.NotSupportedException` will be thrown if you try to assign any type + other than the three types mentioned. + ([#4542](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4542)) + ## 1.5.0 Released 2023-Jun-05 diff --git a/src/OpenTelemetry.Api/Context/RuntimeContext.cs b/src/OpenTelemetry.Api/Context/RuntimeContext.cs index 6ff9699f9f6..a8f45c4052e 100644 --- a/src/OpenTelemetry.Api/Context/RuntimeContext.cs +++ b/src/OpenTelemetry.Api/Context/RuntimeContext.cs @@ -27,10 +27,38 @@ public static class RuntimeContext { private static readonly ConcurrentDictionary Slots = new(); + private static Type contextSlotType = typeof(AsyncLocalRuntimeContextSlot<>); + /// /// Gets or sets the actual context carrier implementation. /// - public static Type ContextSlotType { get; set; } = typeof(AsyncLocalRuntimeContextSlot<>); + public static Type ContextSlotType + { + get => contextSlotType; + set + { + Guard.ThrowIfNull(value, nameof(value)); + + if (value == typeof(AsyncLocalRuntimeContextSlot<>)) + { + contextSlotType = value; + } + else if (value == typeof(ThreadLocalRuntimeContextSlot<>)) + { + contextSlotType = value; + } +#if NETFRAMEWORK + else if (value == typeof(RemotingRuntimeContextSlot<>)) + { + contextSlotType = value; + } +#endif + else + { + throw new NotSupportedException($"{value} is not a supported type."); + } + } + } /// /// Register a named context slot. @@ -41,6 +69,7 @@ public static class RuntimeContext public static RuntimeContextSlot RegisterSlot(string slotName) { Guard.ThrowIfNullOrEmpty(slotName); + RuntimeContextSlot slot = null; lock (Slots) { @@ -49,9 +78,22 @@ public static RuntimeContextSlot RegisterSlot(string slotName) throw new InvalidOperationException($"Context slot already registered: '{slotName}'"); } - var type = ContextSlotType.MakeGenericType(typeof(T)); - var ctor = type.GetConstructor(new Type[] { typeof(string) }); - var slot = (RuntimeContextSlot)ctor.Invoke(new object[] { slotName }); + if (ContextSlotType == typeof(AsyncLocalRuntimeContextSlot<>)) + { + slot = new AsyncLocalRuntimeContextSlot(slotName); + } + else if (ContextSlotType == typeof(ThreadLocalRuntimeContextSlot<>)) + { + slot = new ThreadLocalRuntimeContextSlot(slotName); + } + +#if NETFRAMEWORK + else if (ContextSlotType == typeof(RemotingRuntimeContextSlot<>)) + { + slot = new RemotingRuntimeContextSlot(slotName); + } +#endif + Slots[slotName] = slot; return slot; } diff --git a/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs b/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs index 2b9cf77d9c3..4db5b903352 100644 --- a/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs +++ b/test/OpenTelemetry.AotCompatibility.Tests/AotCompatibilityTests.cs @@ -85,7 +85,7 @@ public void EnsureAotCompatibility() Assert.True(process.ExitCode == 0, "Publishing the AotCompatibility app failed. See test output for more details."); var warnings = expectedOutput.ToString().Split('\n', '\r').Where(line => line.Contains("warning IL")); - Assert.Equal(43, warnings.Count()); + Assert.Equal(40, warnings.Count()); } } }