如何让库实现与本地AOT编译的兼容性?
摘要:原文 | Eric Erhardt 翻译 | 郑子铭 开放式遥测 OpenTelemetry 是一个可观察性框架,允许开发人员从外部了解他们的系统。它在云应用程序中很流行,并且是云原生计算基金会的一部分。 .NET OpenTelemetr
原文 | Eric Erhardt
翻译 | 郑子铭
开放式遥测
OpenTelemetry 是一个可观察性框架,允许开发人员从外部了解他们的系统。它在云应用程序中很流行,并且是云原生计算基金会的一部分。 .NET OpenTelemetry 库必须修复一些地方才能与 AOT 兼容。 open-telemetry/opentelemetry-dotnet#3429 是跟踪必要修复的主要 GitHub 问题。
第一个阻止该库在本机 AOT 应用程序中使用的修复是 open-telemetry/opentelemetry-dotnet#4542。问题是使用工具无法静态分析的值类型调用 MakeGenericType。
当调用 RegisterSlot() 或 RegisterSlot() 时,此代码使用反射动态填充泛型类型,然后调用 ContextSlotType 的构造函数。由于此 API 是公共的,因此可以在 ContextSlotType 上设置任何开放的通用类型。然后任何值类型都可以填充到 RegisterSlot 方法中。
修复方法是进行一个小的重大更改,并且只接受在 ContextSlotType 上设置 2 或 3 个特定类型,这实际上是客户使用的唯一类型。
这些类型是硬编码的,因此不会被删除。现在,AOT 工具可以看到完成这项工作所需的所有代码。
另一个问题是如何在 ActivityInstrumentationHelper 类中使用 System.Linq.Expressions。这是使用私有反射来解决没有公共 API 的另一种情况。 open-telemetry/opentelemetry-dotnet#4513 更改了表达式代码以确保保留必要的属性。
修剪工具无法静态确定 Expression.Property(Expression, string propertyName) 引用了哪个属性,并且 API 已被注释以在调用它时生成警告。相反,如果您使用重载 Expression.Property(Expression, PropertyInfo) 并以工具可以理解的方式获取 PropertyInfo,则可以使代码修剪兼容。
然后使用 open-telemetry/opentelemetry-dotnet#4695 完全删除库中的 System.Linq.Expressions 使用。
虽然表达式可以在本机 AOT 应用程序中使用,但当您 Lambda.Compile() 表达式时,它会使用解释器来计算表达式。这并不理想,并且可能导致性能下降。如果可能,建议在本机 AOT 应用程序中删除 Expression.Compile() 的使用。
接下来是修剪警告的常见误报案例。使用 EventSource 时,通常会将 3 个以上的原始值或不同类型的值传递给 WriteEvent 方法。但是,当您与原始重载不匹配时,您就会陷入使用 object[] args 作为参数的重载。由于这些值是使用反射进行序列化的,因此该 API 带有 [RequiresUnreferencedCode] 注释,并在调用时发出警告。打开 open-telemetry/opentelemetry-dotnet#4428 以添加这些抑制。
这种误报发生的频率非常高,因此 .NET 8 中的 EventSource 中的新 API 使这种误报几乎完全消失。
open-telemetry/opentelemetry-dotnet#4688 中进行了另一个简单的修复,以使 [DynamicallyAccessedMembers] 属性通过库。例如:
接下来,OpenTelemetry 中的几个导出器使用 JSON 序列化将对象数组转换为字符串。如前所述,在没有 JsonTypeInfo 的情况下使用 JsonSerializer.Serialize 与修剪或 AOT 不兼容。 open-telemetry/opentelemetry-dotnet#4679 将这些位置转换为使用 OpenTelemetry 中的 System.Text.Json 源生成器。
