From b9e3d880b5e538e9925b9520e94bbb4349467d27 Mon Sep 17 00:00:00 2001 From: Yun-Ting Lin Date: Mon, 10 Oct 2022 13:58:57 -0700 Subject: [PATCH] [Instrumentation.Process] Implement threadSafe Refresh() (#664) --- .../MeterProviderBuilderExtensions.cs | 2 +- .../ProcessMetrics.cs | 48 ++++++++++++------- .../ProcessMetricsTests.cs | 47 ++++++++++++++++++ 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Process/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Process/MeterProviderBuilderExtensions.cs index 516945ebd1..223808b4a4 100644 --- a/src/OpenTelemetry.Instrumentation.Process/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Process/MeterProviderBuilderExtensions.cs @@ -41,7 +41,7 @@ public static MeterProviderBuilder AddProcessInstrumentation( configure?.Invoke(options); var instrumentation = new ProcessMetrics(options); - builder.AddMeter(ProcessMetrics.MeterInstance.Name); + builder.AddMeter(instrumentation.MeterInstance.Name); return builder.AddInstrumentation(() => instrumentation); } } diff --git a/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs b/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs index 563ff11452..f4e1c74283 100644 --- a/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs @@ -20,42 +20,56 @@ namespace OpenTelemetry.Instrumentation.Process; -internal class ProcessMetrics +internal sealed class ProcessMetrics { internal static readonly AssemblyName AssemblyName = typeof(ProcessMetrics).Assembly.GetName(); - internal static readonly Meter MeterInstance = new(AssemblyName.Name, AssemblyName.Version.ToString()); - private static readonly Diagnostics.Process CurrentProcess = Diagnostics.Process.GetCurrentProcess(); + internal readonly Meter MeterInstance = new(AssemblyName.Name, AssemblyName.Version.ToString()); - static ProcessMetrics() + private readonly Diagnostics.Process currentProcess = Diagnostics.Process.GetCurrentProcess(); + private double? memoryUsage; + private double? virtualMemoryUsage; + + public ProcessMetrics(ProcessInstrumentationOptions options) { // TODO: change to ObservableUpDownCounter - MeterInstance.CreateObservableGauge( + this.MeterInstance.CreateObservableGauge( "process.memory.usage", () => { - CurrentProcess.Refresh(); - return CurrentProcess.WorkingSet64; + if (!this.memoryUsage.HasValue) + { + this.Snapshot(); + } + + var value = this.memoryUsage.Value; + this.memoryUsage = null; + return value; }, unit: "By", - description: "The amount of physical memory in use."); + description: "The amount of physical memory allocated for this process."); // TODO: change to ObservableUpDownCounter - MeterInstance.CreateObservableGauge( + this.MeterInstance.CreateObservableGauge( "process.memory.virtual", () => { - CurrentProcess.Refresh(); - return CurrentProcess.VirtualMemorySize64; + if (!this.virtualMemoryUsage.HasValue) + { + this.Snapshot(); + } + + var value = this.virtualMemoryUsage.Value; + this.virtualMemoryUsage = null; + return value; }, unit: "By", - description: "The amount of committed virtual memory."); + description: "The amount of virtual memory allocated for this process that cannot be shared with other processes."); } - /// - /// Initializes a new instance of the class. - /// - /// The options to define the metrics. - public ProcessMetrics(ProcessInstrumentationOptions options) + private void Snapshot() { + this.currentProcess.Refresh(); + this.memoryUsage = this.currentProcess.WorkingSet64; + this.virtualMemoryUsage = this.currentProcess.PrivateMemorySize64; } } diff --git a/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs b/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs index e227506a81..7adc9fa789 100644 --- a/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs +++ b/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs @@ -15,6 +15,7 @@ // using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Linq; using OpenTelemetry.Metrics; using Xunit; @@ -42,4 +43,50 @@ public void ProcessMetricsAreCaptured() var virtualMemoryMetric = exportedItems.FirstOrDefault(i => i.Name == "process.memory.virtual"); Assert.NotNull(virtualMemoryMetric); } + + [Fact] + public void CheckValidGaugeValueWhen2MeterProviderInstancesHaveTheSameMeterName() + { + var exportedItemsA = new List(); + var exportedItemsB = new List(); + + using var meterProviderA = Sdk.CreateMeterProviderBuilder() + .AddProcessInstrumentation() + .AddInMemoryExporter(exportedItemsA) + .Build(); + + using var meterProviderB = Sdk.CreateMeterProviderBuilder() + .AddProcessInstrumentation() + .AddInMemoryExporter(exportedItemsB) + .Build(); + + meterProviderA.ForceFlush(MaxTimeToAllowForFlush); + meterProviderB.ForceFlush(MaxTimeToAllowForFlush); + + var metricA = exportedItemsA.FirstOrDefault(i => i.Name == "process.memory.usage"); + var metricB = exportedItemsB.FirstOrDefault(i => i.Name == "process.memory.usage"); + + Assert.True(GetValue(metricA) > 0); + Assert.True(GetValue(metricB) > 0); + } + + private static double GetValue(Metric metric) + { + Assert.NotNull(metric); + double sum = 0; + + foreach (ref readonly var metricPoint in metric.GetMetricPoints()) + { + if (metric.MetricType.IsGauge()) + { + sum += metricPoint.GetGaugeLastValueDouble(); + } + else if (metric.MetricType.IsDouble()) + { + sum += metricPoint.GetSumDouble(); + } + } + + return sum; + } }