opentelemetry-dotnet: OpenTelemetry .NET SDK is not AOT safe

Bug Report

When publishing my app with dotnet publish --self-contained -p:PublishAot=true -r win-x64 -o out and then running it, the app crashes on startup with an open telemetry stack:

Unhandled Exception: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
 ---> System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
 ---> System.Reflection.MissingMetadataException: 'OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<System.Int32>' is missing metadata. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=392859
   at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeConstructedGenericTypeInfo, RuntimeTypeInfo[]) + 0x15a
   at System.Reflection.Runtime.General.TypeUnifier.GetConstructedGenericTypeWithTypeHandle(RuntimeTypeInfo, RuntimeTypeInfo[]) + 0x3b
   at System.Reflection.Runtime.TypeInfos.RuntimeTypeInfo.MakeGenericType(Type[]) + 0x40d
   at OpenTelemetry.Context.RuntimeContext.RegisterSlot[T](String) + 0x161
   at OpenTelemetry.SuppressInstrumentationScope..cctor() + 0x1d
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x16a
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x26b
   at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnGCStaticBase(StaticClassConstructionContext*, Object) + 0x1c
   at OpenTelemetry.SuppressInstrumentationScope.get_IsSuppressed() + 0x1b
   at OpenTelemetry.Sdk.get_SuppressInstrumentation() + 0x15
   at OpenTelemetry.Logs.OpenTelemetryLoggerProvider..cctor() + 0x10
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x16a
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x26b
   at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x1c
   at OpenTelemetry.Logs.OpenTelemetryLoggerProvider..ctor(IOptionsMonitor`1) + 0x29

libraries:

    <PackageReference Update="OpenTelemetry" Version="[1.2.0-rc3]" />
    <PackageReference Update="OpenTelemetry.Api" Version="[1.2.0-rc3]" />
    <PackageReference Update="OpenTelemetry.Exporter.Console" Version="[1.2.0-rc3]" />
    <PackageReference Update="OpenTelemetry.Exporter.Prometheus" Version="[1.2.0-rc3]" />

more information: https://docs.microsoft.com/en-us/dotnet/core/deploying/native-aot

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 5
  • Comments: 18 (15 by maintainers)

Commits related to this issue

Most upvoted comments

@Yun-Ting would be working on this issue.

I’ve moved this from the 1.5.0 milestone. We’re continuing to make progress on AOT support, but we will not be able to have full support in the 1.5 timeframe. We still anticipate full support in time for supporting .NET 8.

Hey all – I own Native AOT and would love to help here, so let me know if you have any questions. I agree with @eerhardt that our general recommendation is to try to fix AOT and trimming warnings by removing areas where runtime code generation or reflection are not strictly necessary.

If you want to be confident that you’re trimming and AOT compatible, I would suggest adding the following properties to your project file and compiling for >= .NET 6:

<IsTrimmable>true</IsTrimmable>
<EnableAotAnalyzer>true</EnableAotAnalyzer>

This should catch the vast majority of trimming and AOT problems.

@reyang @cijothomas @CodeBlanch - I ran into this issue as well trying to use Open Telemetry in an ASP.NET app. We want to enable ASP.NET + NativeAOT in .NET 8.0. See https://github.com/dotnet/aspnetcore/issues/45910. Ensuring Open Telemetry works in these NativeAOT ASP.NET apps is part of this goal.

The issue is this following code:

https://github.com/open-telemetry/opentelemetry-dotnet/blob/a878ca3eead7d1b84c3e4c379c81b665b553c41b/src/OpenTelemetry.Api/Context/RuntimeContext.cs#L33-L55

This is not NativeAOT-compatible since it using Reflection to MakeGenericType on the ContextSlotType. The NativeAOT compiler has no idea that it needs to generate native code for the AsyncLocalRuntimeContextSlot<int> type. And thus we get an exception at runtime above.

You also get warnings about this method when you publish with -p:PublishAot=true.

ILC : Trim analysis warning IL2055: OpenTelemetry.Context.RuntimeContext.RegisterSlot<T>(String): Call to 'System.Type.MakeGenericType(Type[])' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic type. [C:\DotNetTest\Net8Api\Net8Api.csproj]
ILC : AOT analysis warning IL3050: OpenTelemetry.Context.RuntimeContext.RegisterSlot<T>(String): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [C:\DotNetTest\Net8Api\Net8Api.csproj]
ILC : Trim analysis warning IL2075: OpenTelemetry.Context.RuntimeContext.RegisterSlot<T>(String): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors' in call to 'System.Type.GetConstructor(Type[])'. The return value of method 'OpenTelemetry.Context.RuntimeContext.ContextSlotType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. [C:\DotNetTest\Net8Api\Net8Api.csproj]

What can we do to solve this? We need to not use MakeGenericType, and instead have a static call to the RuntimeContextSlot<T> that we are trying to create.

Do we really need the full extensibility of public static Type ContextSlotType { get; set; }? Would it be acceptable to remove that API and instead have a static enum that could be set for the 3 types of context slots? Then RegisterSlot could look something like:

    public enum ContextSlotType
    {
        AsyncLocal,
        ThreadLocal,
        Remoting
    }
...
        public static RuntimeContextSlot<T> RegisterSlot<T>(string slotName)
        {
            Guard.ThrowIfNullOrEmpty(slotName);

            lock (Slots)
            {
                if (Slots.ContainsKey(slotName))
                {
                    throw new InvalidOperationException($"Context slot already registered: '{slotName}'");
                }

                RuntimeContextSlot<T> slot = ContextSlotType switch
                {
                    ContextSlotType.AsyncLocal => new AsyncLocalRuntimeContextSlot<T>(slotName),
                    ContextSlotType.ThreadLocal => new ThreadLocalRuntimeContextSlot<T>(slotName),
#if NETFRAMEWORK
                    ContextSlotType.Remoting => new RemotingRuntimeContextSlot<T>(slotName),
#endif
                    _ => throw new NotSupportedException($"{ContextSlotType} is not supported."),
                };
                Slots[slotName] = slot;
                return slot;
            }
        }

cc @LakshanF @agocke @tarekgh

I am getting an error when using

		<PackageReference Include="OpenTelemetry" Version="1.6.0" />
		<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.6.0" />

Error:

Unknown error in export method: {0}{System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
 ---> System.PlatformNotSupportedException: Dynamic code generation is not supported on this platform.
   at System.Reflection.Emit.ReflectionEmitThrower.ThrowPlatformNotSupportedException() + 0x2b
   at OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.MetricItemExtensions.CreateRepeatedFieldOfMetricSetCountAction() + 0x8b
   at OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.MetricItemExtensions..cctor() + 0x6e
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb9
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb0
   at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnGCStaticBase(StaticClassConstructionContext*, Object) + 0xd
   at OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.MetricItemExtensions.AddMetrics(ExportMetricsServiceRequest request, Resource processResource, Batch`1& metrics) + 0x11a
   at OpenTelemetry.Exporter.OtlpMetricExporter.Export(??? metrics) + 0xd9}