Summary
Add overloads to Sentry.SentrySdk.Metrics.Emit* methods with a by-ref System.Diagnostics.TagList parameter for Sentry Attributes.
Remarks
Added in .NET 6.0, and via the out-of-band NuGet package System.Diagnostics.DiagnosticSource, the System.Diagnostics.TagList is a collection of KeyValuePair<string, object?> that stores up to 8 (eight) tags within this struct, and only allocates contiguous regions of memory on the heap when representing more than 8 (eight) tags (starting with an array of 16 elements, growing again by eight when exceeded).
The TagList is used widely across the System.Diagnostics.Metrics APIs.
We can provide an overload to the EmitCounter, EmitGauge and EmitDistribution method groups so that callers can make use of this type, too, by representing Sentry Attributes.
A common scenario is when collecting/exporting Meter-based Metrics via the System.Diagnostics.Metrics.MeterListener, that we are currently prototyping.
We should pass this struct ByRef rather than ByVal, as it's a larger struct. Preferred in a readonly fashion, where in might be the best fit (because an rvalue also makes sense semantically since we don't necessarily require a location). But make sure to only invoke readonly members of that struct, as it's not a readonly struct.
Also, consume via a for loop rather than a foreach loop, because the implicit GetEnumerator implementation does not return the struct-based TagList.Enumerator, but just the IEnumerator<KeyValuePair<string, object?>> interface, which is allocating a box for the struct on the heap on runtimes that cannot de-virtualize the call via dynamic PGO.
See blog Performance Improvements in .NET 10.
Potential follow-up
Propose to dotnet/runtime to expose the internal Tags property, returning ReadOnlySpan<KeyValuePair<string, object?>>, via an unsafe Marshal type, similar to System.Runtime.InteropServices.AsSpan<>(System.Collections.Generic.List<T>?), highlighting our usage example where we could forward to our already existing internal KeyValuePair<string, object> overloads, rather than providing a whole new code path for in TagList.
Summary
Add overloads to
Sentry.SentrySdk.Metrics.Emit*methods with a by-refSystem.Diagnostics.TagListparameter for Sentry Attributes.Remarks
Added in .NET 6.0, and via the out-of-band NuGet package System.Diagnostics.DiagnosticSource, the System.Diagnostics.TagList is a collection of
KeyValuePair<string, object?>that stores up to 8 (eight) tags within thisstruct, and only allocates contiguous regions of memory on the heap when representing more than 8 (eight) tags (starting with an array of 16 elements, growing again by eight when exceeded).The
TagListis used widely across theSystem.Diagnostics.MetricsAPIs.We can provide an overload to the
EmitCounter,EmitGaugeandEmitDistributionmethod groups so that callers can make use of this type, too, by representing Sentry Attributes.A common scenario is when collecting/exporting
Meter-based Metrics via the System.Diagnostics.Metrics.MeterListener, that we are currently prototyping.We should pass this
structByRefrather thanByVal, as it's a larger struct. Preferred in areadonlyfashion, whereinmight be the best fit (because an rvalue also makes sense semantically since we don't necessarily require a location). But make sure to only invokereadonlymembers of thatstruct, as it's not areadonly struct.Also, consume via a
forloop rather than aforeachloop, because the implicitGetEnumeratorimplementation does not return thestruct-basedTagList.Enumerator, but just theIEnumerator<KeyValuePair<string, object?>>interface, which is allocating a box for thestructon the heap on runtimes that cannot de-virtualize the call via dynamic PGO.See blog Performance Improvements in .NET 10.
Potential follow-up
Propose to
dotnet/runtimeto expose theinternalTagsproperty, returningReadOnlySpan<KeyValuePair<string, object?>>, via an unsafe Marshal type, similar toSystem.Runtime.InteropServices.AsSpan<>(System.Collections.Generic.List<T>?), highlighting our usage example where we could forward to our already existing internalKeyValuePair<string, object>overloads, rather than providing a whole new code path forin TagList.