diff --git a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md index 8228e83493..3539aa3695 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md @@ -4,6 +4,8 @@ * Updated OTel SDK package version to 1.3.0 ([#411](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/411)) +* Fix some bugs in Runtime metrics + ([#409](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/409)) ## 0.1.0-alpha.1 diff --git a/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs b/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs index 955e8f929a..513dfc07d8 100644 --- a/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs @@ -33,6 +33,7 @@ internal class RuntimeMetrics : IDisposable internal static readonly AssemblyName AssemblyName = typeof(RuntimeMetrics).Assembly.GetName(); internal static readonly string InstrumentationName = AssemblyName.Name; internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString(); + private const long NanosecondsPerTick = 100; private static string metricPrefix = "process.runtime.dotnet."; private readonly Meter meter; @@ -46,17 +47,19 @@ public RuntimeMetrics(RuntimeMetricsOptions options) if (options.IsGcEnabled) { + // TODO: Almost all the ObservableGauge should be ObservableUpDownCounter (except for CPU utilization and fragmentation.ratio. + // Replace them once ObservableUpDownCounter is available. this.meter.CreateObservableGauge($"{metricPrefix}gc.heap", () => GC.GetTotalMemory(false), "By", "GC Heap Size"); this.meter.CreateObservableGauge($"{metricPrefix}gen_0-gc.count", () => GC.CollectionCount(0), description: "Gen 0 GC Count"); this.meter.CreateObservableGauge($"{metricPrefix}gen_1-gc.count", () => GC.CollectionCount(1), description: "Gen 1 GC Count"); this.meter.CreateObservableGauge($"{metricPrefix}gen_2-gc.count", () => GC.CollectionCount(2), description: "Gen 2 GC Count"); #if NETCOREAPP3_1_OR_GREATER - this.meter.CreateObservableCounter($"{metricPrefix}alloc.rate", () => GC.GetTotalAllocatedBytes(), "By", "Allocation Rate"); - this.meter.CreateObservableCounter($"{metricPrefix}gc.fragmentation", GetFragmentation, description: "GC Fragmentation"); + this.meter.CreateObservableCounter($"{metricPrefix}gc.allocated.bytes", () => GC.GetTotalAllocatedBytes(), "By", "Allocation Rate"); + this.meter.CreateObservableGauge($"{metricPrefix}gc.fragmentation.ratio", GetFragmentation, description: "GC Fragmentation"); #endif #if NET6_0_OR_GREATER - this.meter.CreateObservableCounter($"{metricPrefix}gc.committed", () => (double)(GC.GetGCMemoryInfo().TotalCommittedBytes / 1_000_000), "Mi", description: "GC Committed Bytes"); + this.meter.CreateObservableGauge($"{metricPrefix}gc.committed", () => GC.GetGCMemoryInfo().TotalCommittedBytes, "By", description: "GC Committed Bytes"); #endif } @@ -65,7 +68,7 @@ public RuntimeMetrics(RuntimeMetricsOptions options) { this.meter.CreateObservableCounter($"{metricPrefix}il.bytes.jitted", () => System.Runtime.JitInfo.GetCompiledILBytes(), "By", description: "IL Bytes Jitted"); this.meter.CreateObservableCounter($"{metricPrefix}methods.jitted.count", () => System.Runtime.JitInfo.GetCompiledMethodCount(), description: "Number of Methods Jitted"); - this.meter.CreateObservableGauge($"{metricPrefix}time.in.jit", () => System.Runtime.JitInfo.GetCompilationTime().TotalMilliseconds, "ms", description: "Time spent in JIT"); + this.meter.CreateObservableGauge($"{metricPrefix}time.in.jit", () => System.Runtime.JitInfo.GetCompilationTime().Ticks * NanosecondsPerTick, "ns", description: "Time spent in JIT"); } #endif @@ -73,26 +76,26 @@ public RuntimeMetrics(RuntimeMetricsOptions options) if (options.IsThreadingEnabled) { this.meter.CreateObservableGauge($"{metricPrefix}monitor.lock.contention.count", () => Monitor.LockContentionCount, description: "Monitor Lock Contention Count"); - this.meter.CreateObservableCounter($"{metricPrefix}threadpool.thread.count", () => ThreadPool.ThreadCount, description: "ThreadPool Thread Count"); + this.meter.CreateObservableGauge($"{metricPrefix}threadpool.thread.count", () => (long)ThreadPool.ThreadCount, description: "ThreadPool Thread Count"); this.meter.CreateObservableGauge($"{metricPrefix}threadpool.completed.items.count", () => ThreadPool.CompletedWorkItemCount, description: "ThreadPool Completed Work Item Count"); - this.meter.CreateObservableCounter($"{metricPrefix}threadpool.queue.length", () => ThreadPool.PendingWorkItemCount, description: "ThreadPool Queue Length"); - this.meter.CreateObservableCounter($"{metricPrefix}active.timer.count", () => Timer.ActiveCount, description: "Number of Active Timers"); + this.meter.CreateObservableGauge($"{metricPrefix}threadpool.queue.length", () => ThreadPool.PendingWorkItemCount, description: "ThreadPool Queue Length"); + this.meter.CreateObservableGauge($"{metricPrefix}active.timer.count", () => Timer.ActiveCount, description: "Number of Active Timers"); } #endif if (options.IsProcessEnabled) { - this.meter.CreateObservableCounter("process.cpu.time", this.GetProcessorTimes, "s", "Processor time of this process"); + this.meter.CreateObservableCounter("process.cpu.time", this.GetProcessorTimes, "ns", "Processor time of this process"); // Not yet official: https://github.com/open-telemetry/opentelemetry-specification/pull/2392 - this.meter.CreateObservableGauge("process.cpu.count", () => Environment.ProcessorCount, description: "The number of available logical CPUs"); + this.meter.CreateObservableGauge("process.cpu.count", () => (long)Environment.ProcessorCount, description: "The number of available logical CPUs"); this.meter.CreateObservableGauge("process.memory.usage", () => Process.GetCurrentProcess().WorkingSet64, "By", "The amount of physical memory in use"); this.meter.CreateObservableGauge("process.memory.virtual", () => Process.GetCurrentProcess().VirtualMemorySize64, "By", "The amount of committed virtual memory"); } if (options.IsAssembliesEnabled) { - this.meter.CreateObservableCounter($"{metricPrefix}assembly.count", () => AppDomain.CurrentDomain.GetAssemblies().Length, description: "Number of Assemblies Loaded"); + this.meter.CreateObservableCounter($"{metricPrefix}assembly.count", () => (long)AppDomain.CurrentDomain.GetAssemblies().Length, description: "Number of Assemblies Loaded"); } } @@ -106,17 +109,17 @@ public void Dispose() private static double GetFragmentation() { var gcInfo = GC.GetGCMemoryInfo(); - return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0; + return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 1.0d / gcInfo.HeapSizeBytes : 0; } #endif - private IEnumerable> GetProcessorTimes() + private IEnumerable> GetProcessorTimes() { var process = Process.GetCurrentProcess(); return new[] { - new Measurement(process.UserProcessorTime.TotalSeconds, new KeyValuePair("state", "user")), - new Measurement(process.PrivilegedProcessorTime.TotalSeconds, new KeyValuePair("state", "system")), + new Measurement(process.UserProcessorTime.Ticks * NanosecondsPerTick, new KeyValuePair("state", "user")), + new Measurement(process.PrivilegedProcessorTime.Ticks * NanosecondsPerTick, new KeyValuePair("state", "system")), }; } } diff --git a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs index 997a62f6d6..618ca36614 100644 --- a/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs +++ b/test/OpenTelemetry.Instrumentation.Runtime.Tests/RuntimeMetricsTests.cs @@ -78,7 +78,7 @@ public void ProcessMetricsAreCaptured() Assert.Equal(4, exportedItems.Count); var cpuTimeMetric = exportedItems.First(i => i.Name == "process.cpu.time"); - var sumReceived = GetDoubleSum(cpuTimeMetric); + var sumReceived = GetLongSum(cpuTimeMetric); Assert.True(sumReceived > 0); var cpuCountMetric = exportedItems.First(i => i.Name == "process.cpu.count"); @@ -91,25 +91,6 @@ public void ProcessMetricsAreCaptured() Assert.True(GetLongSum(virtualMemoryMetric) > 0); } - private static double GetDoubleSum(Metric metric) - { - double sum = 0; - - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - if (metric.MetricType.IsSum()) - { - sum += metricPoint.GetSumDouble(); - } - else - { - sum += metricPoint.GetGaugeLastValueDouble(); - } - } - - return sum; - } - private static double GetLongSum(Metric metric) { double sum = 0;