-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initializing RuntimeMetrics #105845
Comments
It does not. You have to use regular reflection ( |
DiagnosticSource is OOB. You need to worry about versions mixing and matching (e.g. .NET 9 runtime using .NET 10 version of DiagnosticSource). We tend to avoid hidden contracts for OOB. You can certainly do it, but our infrastructure won't detect when you make a mistake and remove the method by accident. |
Can we use any public type in DiagnosticSource (like |
Fixes dotnet#105845 Previously MetricsEventSource wasn't being created for apps that didn't ever create a Meter. This caused a chicken-and-egg problem for RuntimeMetrics which weren't created until MetricsEventSource started a tracing session. This change ensures that MetricsEventSource will be created on demand if ETW, EventPipe, or EventListener based tooling starts a tracing session. I took some extra effort to create the EventSource in a deferred fashion to avoid eager loading System.Diagnostics.DiagnosticSource.dll when it might never be needed. Aside from the fix there were some small improvements: - Moved NativeRuntimeEventSource to initialize in the same place as other startup EventSources - Removed a useless lock(EventListener.EventListenersLock) around EventPipe eventProvider registration
Fixes dotnet#105845 Previously MetricsEventSource wasn't being created for apps that didn't ever create a Meter. This caused a chicken-and-egg problem for RuntimeMetrics which weren't created until MetricsEventSource started a tracing session. This change ensures that MetricsEventSource will be created on demand if ETW, EventPipe, or EventListener based tooling starts a tracing session. I took some extra effort to create the EventSource in a deferred fashion to avoid eager loading System.Diagnostics.DiagnosticSource.dll when it might never be needed. Aside from the fix there were some small improvements: - Moved NativeRuntimeEventSource to initialize in the same place as other startup EventSources - Removed a useless lock(EventListener.EventListenersLock) around EventPipe eventProvider registration
I have a PR ready for review to resolve this.
I didn't go that route because the deferred loading mechanism I created requires that I can get a reference to the EventSource being created. Using a DiagnosticSource ModuleInitializer would get MetricsEventSource created, but wouldn't make the reference to it available. If you think there is a better option I'm still glad to hear it - we should probably move discussion about implementation over to the PR though. |
Description
We recently added RuntimeMetrics, part of our long-running plan to offer all runtime metrics using OpenTelemetry standardized semantic conventions + the new Meter API added in .NET 6. For most metrics it is expected the Meter gets created either because the developer directly invoked new Meter(), or they invoked some higher level API that indirectly creates the Meter on their behalf. However we expect RuntimeMetrics to be available in all .NET apps by default without the developer invoking any explicit API to match the existing behavior available with runtime EventCounters. In particular we expect this repro scenario to work:
Repro
dotnet new console
and modify it so that it doesn't exit immediately like this:dotnet run
dotnet-counters monitor -n <name_of_app>
Expected behavior
dotnet-counters should display the new metrics:
Actual behavior
dotnet-counters shows the pre-existing EventCounters
Cause
In order to create the RuntimeMetrics automatically the current implementation initializes the Meter in response to any MeterListener being created. The premise was that it only matters if the Meter exists when something is capable of listening to it. For dotnet-counters we expect MetricsEventSource to create a MeterListener whenever dotnet-counters starts a new monitoring session.
OnEventCommand triggers the lazy creation of the CommandHandler, which creates AggregationManager, which creates MeterListener. Unforetunately we overlooked that MetricsEventSource itself isn't created unless a Meter exists. This made a chicken-egg situation where RuntimeMetrics Meter depends on MetricsEventSource being created first, but MetricsEventSource is depending on some Meter, such as RuntimeMetrics being created first. Many apps would have code that create some other Meter which breaks the cycle and causes everything to get bootstrapped but a simple console app doesn't do that.
Proposed solution
We need some code in the initialization path of .NET Core apps to ensure that MetricsEventSource is created, similar to what we already do for RuntimeEventSource. Since loading MetricsEventSource will cause another assembly to load (System.Diagnostics.DiagnosticSource) this has some startup performance overhead. We planned to mitigate this overhead by checking both
EventSource.IsSupported
and the AppContext switchSystem.Diagnostics.Metrics.Meter.IsSupported
before doing the load. This would let form factors that are particularly size or startup cost sensitive to opt out of this as they already do for other EventSource/Meter related overhead. For apps that didn't opt-out we would then use reflection to get a delegate for some well-known non-public method in DiagnosticSource.dll, similar to the approach we use for StackTraceSymbols.I see that currently CoreCLR, NativeAOT, and Mono all use slightly different approaches for getting RuntimeEventSource initialized (Mono, NativeAOT, CoreCLR). I assume we'd also do a little bit of refactoring to create some common EventSource initialization method that handles both RuntimeEventSource and MetricsEventSource.
Questions/Feedback
@brianrob @jkotas @MichalStrehovsky @LakshanF @tarekgh
Thanks!
The text was updated successfully, but these errors were encountered: