Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GC heap size and count in Runtime metrics #412

Merged
merged 20 commits into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
77749e7
Add `gc.heapsize` and change `gc.count` to multi-dimensional metrics
xiang17 Jun 8, 2022
0caf3d2
Update CHANGELOG.md
xiang17 Jun 8, 2022
cae39a2
Merge branch 'main' into xiang17/addGCCounter
xiang17 Jun 8, 2022
210708d
Reword CHANGELOG.md message
xiang17 Jun 8, 2022
463a55a
Set automatic name if the number of heap goes beyond 5
xiang17 Jun 8, 2022
0dfadc1
Merge branch 'xiang17/addGCCounter' of https://github.com/xiang17/ope…
xiang17 Jun 8, 2022
41325bf
Merge branch 'main' into xiang17/addGCCounter
xiang17 Jun 9, 2022
cb74640
Allocate measurement arrays during initialization as static fields to…
xiang17 Jun 9, 2022
b767996
Merge branch 'xiang17/addGCCounter' of https://github.com/xiang17/ope…
xiang17 Jun 9, 2022
72ee40f
Update unit tests according for the value type change for active.time…
xiang17 Jun 9, 2022
ac77d17
Sharing the variable is not thread safe in case multiple exporter acc…
xiang17 Jun 10, 2022
e1ae0dd
Reword
xiang17 Jun 10, 2022
4cb0bcd
Reword
xiang17 Jun 10, 2022
f471e45
Limit max number of generations shown
xiang17 Jun 10, 2022
eef80a1
Use `s` as unit and `double` as value type for CPU time
xiang17 Jun 10, 2022
0d894a1
Update unit tests for CPU unit and value type change
xiang17 Jun 10, 2022
0f23495
Merge branch 'main' into xiang17/addGCCounter
xiang17 Jun 10, 2022
d38fe7a
Rename HeapName to GenName to keep the names consistent
xiang17 Jun 10, 2022
7eb56f7
Merge branch 'main' into xiang17/addGCCounter
xiang17 Jun 10, 2022
bf5f62e
Put `.` at the end of the description text
xiang17 Jun 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Instrumentation.Runtime/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* Updated OTel SDK package version to 1.3.0
([#411](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/411))
* Add GC heap size and refactor GC count as multi-dimensional metrics in Runtime
metrics ([#412](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/412))

## 0.1.0-alpha.1

Expand Down
52 changes: 42 additions & 10 deletions src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ 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 static readonly string[] HeapNames = new string[] { "gen0", "gen1", "gen2", "loh", "poh" };
private static readonly int NumberOfGenerations = 3;
private static readonly Measurement<long>[] GarbageCollectionCountMeasurements = new Measurement<long>[NumberOfGenerations];
#if NET6_0_OR_GREATER
private static readonly Measurement<long>[] HeapSizeMeasurements = new Measurement<long>[GC.GetGCMemoryInfo().GenerationInfo.Length];
xiang17 marked this conversation as resolved.
Show resolved Hide resolved
#endif
private static readonly Measurement<long>[] ProcessorTimeMeasurements = new Measurement<long>[2];
private static string metricPrefix = "process.runtime.dotnet.";
private readonly Meter meter;

Expand All @@ -47,16 +54,16 @@ public RuntimeMetrics(RuntimeMetricsOptions options)
if (options.IsGcEnabled)
{
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");
this.meter.CreateObservableGauge($"{metricPrefix}gc.count", () => GetGarbageCollectionCounts(), description: "GC Count for all generations");

#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");
#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.heapsize", () => GetGarbageCollectionHeapSizes(), "By", "Heap Size for all generations");
xiang17 marked this conversation as resolved.
Show resolved Hide resolved
#endif
}

Expand All @@ -82,7 +89,7 @@ public RuntimeMetrics(RuntimeMetricsOptions options)

if (options.IsProcessEnabled)
{
this.meter.CreateObservableCounter("process.cpu.time", this.GetProcessorTimes, "s", "Processor time of this process");
this.meter.CreateObservableCounter("process.cpu.time", GetProcessorTimes, "ns", "Processor time of this process");
xiang17 marked this conversation as resolved.
Show resolved Hide resolved

// 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");
Expand All @@ -102,6 +109,16 @@ public void Dispose()
this.meter?.Dispose();
}

private static IEnumerable<Measurement<long>> GetGarbageCollectionCounts()
{
for (int i = 0; i < GarbageCollectionCountMeasurements.Length; ++i)
{
GarbageCollectionCountMeasurements[i] = new Measurement<long>(GC.CollectionCount(i), new KeyValuePair<string, object>("gen", HeapNames[i]));
}

return GarbageCollectionCountMeasurements;
}

#if NETCOREAPP3_1_OR_GREATER
private static double GetFragmentation()
{
Expand All @@ -110,14 +127,29 @@ private static double GetFragmentation()
}
#endif

private IEnumerable<Measurement<double>> GetProcessorTimes()
#if NET6_0_OR_GREATER
private static IEnumerable<Measurement<long>> GetGarbageCollectionHeapSizes()
{
var process = Process.GetCurrentProcess();
return new[]
var generationInfo = GC.GetGCMemoryInfo().GenerationInfo;
xiang17 marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Confirm 1. that the number of heaps for garbage collection will not change, at least during the lifetime of a process.
// and 2. that there will not be more than 5 heaps, at least for the existing .NET version that is supported (net6.0).
Debug.Assert(generationInfo.Length <= HeapNames.Length, "There should not be more than 5 heaps");
xiang17 marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < HeapSizeMeasurements.Length; ++i)
{
new Measurement<double>(process.UserProcessorTime.TotalSeconds, new KeyValuePair<string, object>("state", "user")),
new Measurement<double>(process.PrivilegedProcessorTime.TotalSeconds, new KeyValuePair<string, object>("state", "system")),
};
HeapSizeMeasurements[i] = new Measurement<long>(generationInfo[i].SizeAfterBytes, new KeyValuePair<string, object>("gen", HeapNames[i]));
}

return HeapSizeMeasurements;
}
#endif

private static IEnumerable<Measurement<long>> GetProcessorTimes()
{
var process = Process.GetCurrentProcess();
ProcessorTimeMeasurements[0] = new Measurement<long>(process.UserProcessorTime.Ticks * 100, new KeyValuePair<string, object>("state", "user"));
ProcessorTimeMeasurements[1] = new Measurement<long>(process.PrivilegedProcessorTime.Ticks * 100, new KeyValuePair<string, object>("state", "system"));
return ProcessorTimeMeasurements;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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;
Expand Down