diff --git a/src/OpenTelemetry.Instrumentation.Process/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.Process/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 0247a31728..8066d220fa 100644 --- a/src/OpenTelemetry.Instrumentation.Process/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.Process/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ -OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions -OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions.ProcessInstrumentationOptions() -> void -OpenTelemetry.Metrics.MeterProviderBuilderExtensions -static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddProcessInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions +OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions.CpuStatesEnabled.get -> bool? +OpenTelemetry.Instrumentation.Process.ProcessInstrumentationOptions.ProcessInstrumentationOptions() -> void +OpenTelemetry.Metrics.MeterProviderBuilderExtensions +static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddProcessInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.Process/ProcessInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Process/ProcessInstrumentationOptions.cs index 6b7787aa8a..ec0481abf3 100644 --- a/src/OpenTelemetry.Instrumentation.Process/ProcessInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.Process/ProcessInstrumentationOptions.cs @@ -20,6 +20,10 @@ namespace OpenTelemetry.Instrumentation.Process; /// Options to define the process metrics. /// public class ProcessInstrumentationOptions -{ - public bool? ExpandOnCpuStates { get; set; } +{ + /// + /// Gets the flag indicating whether Cpu time should be further broken down by its states. + /// The Cpu state could be one of the following type: system, user, wait. + /// + public bool? CpuStatesEnabled { get; } } diff --git a/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs b/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs index e96ea57d1b..ee05281009 100644 --- a/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs @@ -14,6 +14,7 @@ // limitations under the License. // +using System.Collections.Generic; using System.Diagnostics.Metrics; using System.Reflection; using Diagnostics = System.Diagnostics; @@ -46,6 +47,23 @@ public ProcessMetrics(ProcessInstrumentationOptions? options) () => InstrumentsValues.GetVirtualMemoryUsage(), unit: "By", description: "The amount of committed virtual memory."); + + if (options.CpuStatesEnabled == false) + { + MeterInstance.CreateObservableCounter( + $"process.cpu.time", + () => InstrumentsValues.GetProcessorCpuTime(), + unit: "s", + description: "Total CPU seconds."); + } + else + { + MeterInstance.CreateObservableCounter( + $"process.cpu.time", + () => InstrumentsValues.GetProcessorCpuTimeWithBreakdown(), + unit: "s", + description: "Total CPU seconds broken down by different states."); + } } private class InstrumentsValues @@ -57,6 +75,13 @@ public InstrumentsValues() CurrentProcess.Refresh(); } + private enum CPUState + { + System, + User, + Wait, + } + public static long GetMemoryUsage() { return CurrentProcess.WorkingSet64; @@ -66,5 +91,24 @@ public static long GetVirtualMemoryUsage() { return CurrentProcess.VirtualMemorySize64; } + + public static long GetProcessorCpuTime() + { + return CurrentProcess.TotalProcessorTime.Seconds; + } + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/process-metrics.md#metric-instruments + public static Measurement[] GetProcessorCpuTimeWithBreakdown() + { + Measurement[] measurements = new Measurement[3]; + var priviledgedCpuTime = CurrentProcess.PrivilegedProcessorTime.Seconds; + var userCpuTime = CurrentProcess.UserProcessorTime.Seconds; + + measurements[(int)CPUState.System] = new(priviledgedCpuTime, new KeyValuePair("state", CPUState.System.ToString())); + measurements[(int)CPUState.User] = new(userCpuTime, new KeyValuePair("state", CPUState.User.ToString())); + measurements[(int)CPUState.Wait] = new(CurrentProcess.TotalProcessorTime.Seconds - priviledgedCpuTime - userCpuTime, new KeyValuePair("state", CPUState.Wait.ToString())); + + return measurements; + } } } diff --git a/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs b/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs index e227506a81..623827be6d 100644 --- a/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs +++ b/test/OpenTelemetry.Instrumentation.Process.Tests/ProcessMetricsTests.cs @@ -36,10 +36,12 @@ public void ProcessMetricsAreCaptured() meterProvider.ForceFlush(MaxTimeToAllowForFlush); - Assert.True(exportedItems.Count == 2); + Assert.True(exportedItems.Count == 3); var physicalMemoryMetric = exportedItems.FirstOrDefault(i => i.Name == "process.memory.usage"); Assert.NotNull(physicalMemoryMetric); var virtualMemoryMetric = exportedItems.FirstOrDefault(i => i.Name == "process.memory.virtual"); Assert.NotNull(virtualMemoryMetric); + var processCpuTimeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.cpu.time"); + Assert.NotNull(processCpuTimeMetric); } }