Skip to content

Consider changing diagnostic events to use public declared types as their payload #48616

Open
@vitek-karas

Description

@vitek-karas

The events produced by ASP.NET to the diagnostic listener pass a payload object which is consumed by several different tools/libraries. For some of the events the payload object uses a public defined type and thus the consumer can directly cast it and access all the relevant data. But for some events the event uses either declared but internal type or just anonymous type, in these cases the consumer has to use reflection to access the relevant data.

This has distinct disadvantages in performance impact and trim/AOT compatibility. Currently for example Open Telemetry library uses several different tools to overcome these:

  • It includes a relatively complicated reflection based "cache" of accessors to make the access to the properties as fast as possible
  • It has to code defensively to make it work in cases the reflection fails
  • The ASP.NET code uses DynamicDependencyAttribute and DynamicallyAccessedMembersAttribute attributes to hint trimmer/AOT compiler to keep properties on the payload object

There are still unsolved problems though. The trimmer compatibility is not verifiable by any tools, since it relies on the combination of attributes in the ASP.NET code and careful reflection usage in the consuming library. The consuming library has to use suppressions which is always fragile.

Another observation is that the shape of the payload object is effectively a public contract, since the consuming libraries hardcodes the name of the properties and then cast the value of those properties to public types (like Exception). If the framework changed the name of the property, it would be an effective breaking change, even though technically it's not changing any public property.

Some events were already changed like this, for example #11730.

What remains based on OpenTelemetry usage:

Currently we mitigate these problems by maintaining annotations on the type used as well as the logging method. These preserve the properties we know of are accessed dynamically (through reflection) by some of the consumers. This list is inherently incomplete and fragile. It also means that the consumer will get trim/AOT warnings due to reflection usage and it has to suppress these relying on annotations in the framework. This relationship is unverifiable by any tooling and thus fragile.

Note that there are other events which seem to have the same problem, they're just not used by OpenTelemetry which is the consumer I've been looking at:

Additionally, in some cases the naming of the properties is inconsistent. For example the public types use typical Pascal casing of property names, e.g. Request or Exception. But most of the anonymous types use lower case names, like exception. This requires the consumers to perform case insensitive search for the property making it less performant (they typically loop over all properties to achieve this).

Related to #45910 and open-telemetry/opentelemetry-dotnet#3429.

/cc @Yun-Ting, @eerhardt, @LakshanF

Metadata

Metadata

Assignees

No one assigned

    Labels

    NativeAOTarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions