From 07f634d82f5f274d95a6dd479248fdc54cb3558d Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Fri, 22 Nov 2024 07:17:26 +0000 Subject: [PATCH] [Instrumentation.Runtime] Prefer the built-in runtime metrics for .NET 9 targets. (#2339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Kiełkowicz Co-authored-by: xiang17 --- .../CHANGELOG.md | 4 ++ .../MeterProviderBuilderExtensions.cs | 8 ++++ ...enTelemetry.Instrumentation.Runtime.csproj | 2 +- .../README.md | 6 +++ .../RuntimeMetricsTests.cs | 42 ++++++++++++++++++- 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md index 65731d372b..eaeddeec72 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md @@ -9,6 +9,10 @@ * Updated OpenTelemetry core component version(s) to `1.10.0`. ([#2317](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2317)) +* Built-in .NET `System.Runtime` metrics are reported for .NET 9 and greater. + For details about each individual metric check [.NET Runtime metrics docs page](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime). + ([#2339](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2339)) + ## 1.9.0 Released 2024-Jun-18 diff --git a/src/OpenTelemetry.Instrumentation.Runtime/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Runtime/MeterProviderBuilderExtensions.cs index 70959d119c..2a9e3c9e7c 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Runtime/MeterProviderBuilderExtensions.cs @@ -11,6 +11,9 @@ namespace OpenTelemetry.Metrics; /// public static class MeterProviderBuilderExtensions { + private const string DotNetRuntimeMeterName = "System.Runtime"; + private static readonly bool Net9OrGreater = Environment.Version.Major >= 9; + /// /// Enables runtime instrumentation. /// @@ -32,6 +35,11 @@ public static MeterProviderBuilder AddRuntimeInstrumentation( { Guard.ThrowIfNull(builder); + if (Net9OrGreater) + { + return builder.AddMeter(DotNetRuntimeMeterName); + } + var options = new RuntimeInstrumentationOptions(); configure?.Invoke(options); diff --git a/src/OpenTelemetry.Instrumentation.Runtime/OpenTelemetry.Instrumentation.Runtime.csproj b/src/OpenTelemetry.Instrumentation.Runtime/OpenTelemetry.Instrumentation.Runtime.csproj index 8f107f48b5..8f349f186a 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/OpenTelemetry.Instrumentation.Runtime.csproj +++ b/src/OpenTelemetry.Instrumentation.Runtime/OpenTelemetry.Instrumentation.Runtime.csproj @@ -14,8 +14,8 @@ - + diff --git a/src/OpenTelemetry.Instrumentation.Runtime/README.md b/src/OpenTelemetry.Instrumentation.Runtime/README.md index 23e7717842..4120618f70 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/README.md +++ b/src/OpenTelemetry.Instrumentation.Runtime/README.md @@ -48,6 +48,12 @@ to the application. ## Metrics +> [!NOTE] +> .NET 9 introduced built-in runtime metrics. As such, when applications target + .NET 9 or greater this package registers a `Meter` to receive the built-in + `System.Runtime` metrics. See the [.NET Runtime metrics documentation](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime) + for details of the metric and attribute names for the built-in metrics. + ### GC related metrics #### process.runtime.dotnet.**gc.collections.count** diff --git a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs index f9f0bc34d1..a4d50c88ac 100644 --- a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs +++ b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs @@ -32,12 +32,21 @@ public void RuntimeMetricsAreCaptured() meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.True(exportedItems.Count > 1); +#if NET9_0_OR_GREATER + var assembliesCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.assembly.count"); + Assert.NotNull(assembliesCountMetric); + + var exceptionsCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.exceptions"); + Assert.NotNull(exceptionsCountMetric); + Assert.True(GetValue(exceptionsCountMetric) >= 1); +#else var assembliesCountMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.assemblies.count"); Assert.NotNull(assembliesCountMetric); var exceptionsCountMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.exceptions.count"); Assert.NotNull(exceptionsCountMetric); Assert.True(GetValue(exceptionsCountMetric) >= 1); +#endif } [Fact] @@ -53,13 +62,23 @@ public void GcMetricsTest() meterProvider.ForceFlush(MaxTimeToAllowForFlush); +#if NET9_0_OR_GREATER + // We don't need to test all metrics here as those are tested in the runtime. + // This is sufficient to validate that the runtime metrics are enabled. + var gcCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.gc.collections"); + Assert.NotNull(gcCountMetric); + + var totalObjectsSize = exportedItems.FirstOrDefault(i => i.Name == "dotnet.gc.heap.total_allocated"); + Assert.NotNull(totalObjectsSize); +#else var gcCountMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.gc.collections.count"); Assert.NotNull(gcCountMetric); var totalObjectsSize = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.gc.objects.size"); Assert.NotNull(totalObjectsSize); +#endif -#if NET +#if NET8_0 var gcAllocationSizeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.gc.allocations.size"); Assert.NotNull(gcAllocationSizeMetric); @@ -90,6 +109,16 @@ public void JitRelatedMetricsTest() meterProvider.ForceFlush(MaxTimeToAllowForFlush); +#if NET9_0_OR_GREATER + var jitCompiledSizeMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.jit.compiled_il.size"); + Assert.NotNull(jitCompiledSizeMetric); + + var jitMethodsCompiledCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.jit.compiled_methods"); + Assert.NotNull(jitMethodsCompiledCountMetric); + + var jitCompilationTimeMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.jit.compilation.time"); + Assert.NotNull(jitCompilationTimeMetric); +#else var jitCompiledSizeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.jit.il_compiled.size"); Assert.NotNull(jitCompiledSizeMetric); @@ -98,6 +127,7 @@ public void JitRelatedMetricsTest() var jitCompilationTimeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.jit.compilation_time"); Assert.NotNull(jitCompilationTimeMetric); +#endif } [Fact] @@ -121,6 +151,15 @@ public async Task ThreadingRelatedMetricsTest() meterProvider.ForceFlush(MaxTimeToAllowForFlush); +#if NET9_0_OR_GREATER + // We don't need to test all metrics here as those are tested in the runtime. + // This is sufficient to validate that the runtime metrics are enabled. + var lockContentionCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.monitor.lock_contentions"); + Assert.NotNull(lockContentionCountMetric); + + var threadCountMetric = exportedItems.FirstOrDefault(i => i.Name == "dotnet.thread_pool.thread.count"); + Assert.NotNull(threadCountMetric); +#else var lockContentionCountMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.monitor.lock_contention.count"); Assert.NotNull(lockContentionCountMetric); @@ -163,6 +202,7 @@ static void TimerCallback(object? _) timers[i].Dispose(); } } +#endif } #endif