diff --git a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md index 97ed41a722..81ff30c13d 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Add a metric `process.runtime.dotnet.gc.duration` for total paused duration in + GC for .NET 7 and greater versions + ([#1239](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1239)) + ## 1.5.0 Released 2023-Jun-06 diff --git a/src/OpenTelemetry.Instrumentation.Runtime/README.md b/src/OpenTelemetry.Instrumentation.Runtime/README.md index 79734bfc79..20ff2da435 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/README.md +++ b/src/OpenTelemetry.Instrumentation.Runtime/README.md @@ -176,6 +176,22 @@ The API used to retrieve the value is: * [GCGenerationInfo.FragmentationAfterBytes Property](https://docs.microsoft.com/dotnet/api/system.gcgenerationinfo.fragmentationafterbytes) Gets the fragmentation in bytes on exit from the reported collection. +#### process.runtime.dotnet.**gc.duration** + +The total amount of time paused in GC since the process start. + +> **Note** +> This metric is only available when targeting .NET 7 or later. + +| Units | Instrument Type | Value Type | Attribute Key(s) | Attribute Values | +|-------|-------------------|------------|------------------|------------------| +| `ns` | ObservableCounter | `Int64` | No Attributes | N/A | + +The API used to retrieve the value is: + +* [GC.GetTotalPauseDuration](https://learn.microsoft.com/dotnet/api/system.gc.gettotalpauseduration) + Gets the total amount of time paused in GC since the beginning of the process. + ### JIT Compiler related metrics These metrics are only available when targeting .NET6 or later. diff --git a/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs b/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs index eb730de55b..bdbd4f2821 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs @@ -120,9 +120,9 @@ static RuntimeMetrics() description: "The heap size (including fragmentation), as observed during the latest garbage collection. The value will be unavailable until at least one garbage collection has occurred."); } - // Not valid until .NET 7 where the bug in the API is fixed. See context in https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/496 if (Environment.Version.Major >= 7) { + // Not valid until .NET 7 where the bug in the API is fixed. See context in https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/496 MeterInstance.CreateObservableUpDownCounter( "process.runtime.dotnet.gc.heap.fragmentation.size", () => @@ -144,6 +144,18 @@ static RuntimeMetrics() }, unit: "bytes", description: "The heap fragmentation, as observed during the latest garbage collection. The value will be unavailable until at least one garbage collection has occurred."); + + // GC.GetTotalPauseDuration() is not available until .NET 7. See context in https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1163 + var mi = typeof(GC).GetMethod("GetTotalPauseDuration", BindingFlags.Public | BindingFlags.Static); + var getTotalPauseDuration = mi?.CreateDelegate>(); + if (getTotalPauseDuration != null) + { + MeterInstance.CreateObservableCounter( + "process.runtime.dotnet.gc.duration", + () => getTotalPauseDuration().Ticks * NanosecondsPerTick, + unit: "ns", + description: "The total amount of time paused in GC since the process start."); + } } MeterInstance.CreateObservableCounter( diff --git a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs index 602d35e5bf..b935e0c7d5 100644 --- a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs +++ b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs @@ -94,6 +94,9 @@ public void GcMetricsTest() { var gcHeapFragmentationSizeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.gc.heap.fragmentation.size"); Assert.NotNull(gcHeapFragmentationSizeMetric); + + var gcDurationMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.gc.duration"); + Assert.NotNull(gcDurationMetric); } #endif }