Skip to content

Commit

Permalink
[Instrumentation.Process] Add CPU metrics (#678)
Browse files Browse the repository at this point in the history
  • Loading branch information
twenzel authored Oct 11, 2022
1 parent b9e3d88 commit cbed25e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/OpenTelemetry.Instrumentation.Process/ProcessMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Reflection;
using Diagnostics = System.Diagnostics;
Expand All @@ -28,6 +29,8 @@ internal sealed class ProcessMetrics
private readonly Diagnostics.Process currentProcess = Diagnostics.Process.GetCurrentProcess();
private double? memoryUsage;
private double? virtualMemoryUsage;
private double? userProcessorTime;
private double? privilegedProcessorTime;

public ProcessMetrics(ProcessInstrumentationOptions options)
{
Expand Down Expand Up @@ -64,12 +67,37 @@ public ProcessMetrics(ProcessInstrumentationOptions options)
},
unit: "By",
description: "The amount of virtual memory allocated for this process that cannot be shared with other processes.");

this.MeterInstance.CreateObservableCounter(
"process.cpu.time",
() =>
{
if (!this.userProcessorTime.HasValue || !this.privilegedProcessorTime.HasValue)
{
this.Snapshot();
}

var userProcessorTimeValue = this.userProcessorTime.Value;
var privilegedProcessorTimeValue = this.privilegedProcessorTime.Value;
this.userProcessorTime = null;
this.privilegedProcessorTime = null;

return new[]
{
new Measurement<double>(userProcessorTimeValue, new KeyValuePair<string, object?>("state", "user")),
new Measurement<double>(privilegedProcessorTimeValue, new KeyValuePair<string, object?>("state", "system")),
};
},
unit: "s",
description: "Total CPU seconds broken down by different states.");
}

private void Snapshot()
{
this.currentProcess.Refresh();
this.memoryUsage = this.currentProcess.WorkingSet64;
this.virtualMemoryUsage = this.currentProcess.PrivateMemorySize64;
this.userProcessorTime = this.currentProcess.UserProcessorTime.TotalSeconds;
this.privilegedProcessorTime = this.currentProcess.PrivilegedProcessorTime.TotalSeconds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,50 @@ 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 cpuTimeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.cpu.time");
Assert.NotNull(cpuTimeMetric);
}

[Fact]
public void CpuTimeMetricsAreCaptured()
{
var exportedItems = new List<Metric>();
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddProcessInstrumentation()
.AddInMemoryExporter(exportedItems)
.Build();

meterProvider.ForceFlush(MaxTimeToAllowForFlush);

var cpuTimeMetric = exportedItems.FirstOrDefault(i => i.Name == "process.cpu.time");
Assert.NotNull(cpuTimeMetric);

var userTimeCaptured = false;
var systemTimeCaptured = false;

var points = cpuTimeMetric.GetMetricPoints().GetEnumerator();
while (points.MoveNext() && (!userTimeCaptured || !systemTimeCaptured))
{
foreach (var tag in points.Current.Tags)
{
if (tag.Key == "state" && tag.Value.ToString() == "user")
{
userTimeCaptured = true;
}
else if (tag.Key == "state" && tag.Value.ToString() == "system")
{
systemTimeCaptured = true;
}
}
}

Assert.True(userTimeCaptured);
Assert.True(systemTimeCaptured);
}

[Fact]
Expand Down

0 comments on commit cbed25e

Please sign in to comment.