From bafe01ae0113fa481788dbb3bdaf73fbe2cd23c5 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sat, 7 Sep 2024 14:20:09 -0700 Subject: [PATCH] Report both CPU and Memory issues --- .../ResourceUtilizationHealthCheck.cs | 34 ++++++++++------ .../ResourceHealthCheckTests.cs | 40 +++---------------- 2 files changed, 27 insertions(+), 47 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization/ResourceUtilizationHealthCheck.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization/ResourceUtilizationHealthCheck.cs index b60a6068b50..26c5b26940e 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization/ResourceUtilizationHealthCheck.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization/ResourceUtilizationHealthCheck.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.ResourceMonitoring; @@ -14,7 +15,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks; /// internal sealed class ResourceUtilizationHealthCheck : IHealthCheck { - private static readonly Task _healthy = Task.FromResult(HealthCheckResult.Healthy()); private readonly ResourceUtilizationHealthCheckOptions _options; private readonly IResourceMonitor _dataTracker; @@ -39,26 +39,36 @@ public ResourceUtilizationHealthCheck(IOptions CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { var utilization = _dataTracker.GetUtilization(_options.SamplingWindow); - if (utilization.CpuUsedPercentage > _options.CpuThresholds.UnhealthyUtilizationPercentage) + IReadOnlyDictionary data = new Dictionary { - return Task.FromResult(HealthCheckResult.Unhealthy("CPU usage is above the limit")); - } + { nameof(utilization.CpuUsedPercentage), utilization.CpuUsedPercentage }, + { nameof(utilization.MemoryUsedPercentage), utilization.MemoryUsedPercentage }, + }; - if (utilization.MemoryUsedPercentage > _options.MemoryThresholds.UnhealthyUtilizationPercentage) + if (utilization.CpuUsedPercentage > _options.CpuThresholds.UnhealthyUtilizationPercentage) { - return Task.FromResult(HealthCheckResult.Unhealthy("Memory usage is above the limit")); + string message = "CPU usage is above the limit"; + + if (utilization.MemoryUsedPercentage > _options.MemoryThresholds.UnhealthyUtilizationPercentage) + { + message = "CPU and Memory usage is above the limit"; + } + + return Task.FromResult(HealthCheckResult.Unhealthy(message, default, data)); } if (utilization.CpuUsedPercentage > _options.CpuThresholds.DegradedUtilizationPercentage) { - return Task.FromResult(HealthCheckResult.Degraded("CPU usage is close to the limit")); - } + string message = "CPU usage is close to the limit"; - if (utilization.MemoryUsedPercentage > _options.MemoryThresholds.DegradedUtilizationPercentage) - { - return Task.FromResult(HealthCheckResult.Degraded("Memory usage is close to the limit")); + if (utilization.MemoryUsedPercentage > _options.MemoryThresholds.DegradedUtilizationPercentage) + { + message = "CPU and Memory usage is close to the limit"; + } + + return Task.FromResult(HealthCheckResult.Degraded(message, default, data)); } - return _healthy; + return Task.FromResult(HealthCheckResult.Healthy(default, data)); } } diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization.Tests/ResourceHealthCheckTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization.Tests/ResourceHealthCheckTests.cs index d6129017d79..3b2c7d98445 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization.Tests/ResourceHealthCheckTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization.Tests/ResourceHealthCheckTests.cs @@ -92,61 +92,31 @@ public class ResourceHealthCheckTests [Theory] [MemberData(nameof(Data))] -#pragma warning disable xUnit1026 // Theory methods should use all of their parameters - public async Task TestCpuChecks(HealthStatus expected, double utilization, ulong _, ulong totalMemory, ResourceUsageThresholds thresholds, string expectedDescription) -#pragma warning restore xUnit1026 // Theory methods should use all of their parameters + public async Task TestCpuAndMemoryChecks(HealthStatus expected, double utilization, ulong memoryUsed, ulong totalMemory, ResourceUsageThresholds thresholds, string expectedDescription) { var systemResources = new SystemResources(1.0, 1.0, totalMemory, totalMemory); var dataTracker = new Mock(); var samplingWindow = TimeSpan.FromSeconds(1); dataTracker .Setup(tracker => tracker.GetUtilization(samplingWindow)) - .Returns(new ResourceUtilization(cpuUsedPercentage: utilization, memoryUsedInBytes: 0, systemResources)); + .Returns(new ResourceUtilization(cpuUsedPercentage: utilization, memoryUsedInBytes: memoryUsed, systemResources)); var checkContext = new HealthCheckContext(); var cpuCheckOptions = new ResourceUtilizationHealthCheckOptions { CpuThresholds = thresholds, - SamplingWindow = samplingWindow - }; - - var options = Microsoft.Extensions.Options.Options.Create(cpuCheckOptions); - var healthCheck = new ResourceUtilizationHealthCheck(options, dataTracker.Object); - var healthCheckResult = await healthCheck.CheckHealthAsync(checkContext); - Assert.Equal(expected, healthCheckResult.Status); - if (healthCheckResult.Status != HealthStatus.Healthy) - { - Assert.Equal("CPU" + expectedDescription, healthCheckResult.Description); - } - } - - [Theory] - [MemberData(nameof(Data))] -#pragma warning disable xUnit1026 // Theory methods should use all of their parameters - public async Task TestMemoryChecks(HealthStatus expected, double _, ulong memoryUsed, ulong totalMemory, ResourceUsageThresholds thresholds, string expectedDescription) -#pragma warning restore xUnit1026 // Theory methods should use all of their parameters - { - var systemResources = new SystemResources(1.0, 1.0, totalMemory, totalMemory); - var dataTracker = new Mock(); - var samplingWindow = TimeSpan.FromSeconds(1); - dataTracker - .Setup(tracker => tracker.GetUtilization(samplingWindow)) - .Returns(new ResourceUtilization(cpuUsedPercentage: 0, memoryUsedInBytes: memoryUsed, systemResources)); - - var checkContext = new HealthCheckContext(); - var memCheckOptions = new ResourceUtilizationHealthCheckOptions - { MemoryThresholds = thresholds, SamplingWindow = samplingWindow }; - var options = Microsoft.Extensions.Options.Options.Create(memCheckOptions); + var options = Microsoft.Extensions.Options.Options.Create(cpuCheckOptions); var healthCheck = new ResourceUtilizationHealthCheck(options, dataTracker.Object); var healthCheckResult = await healthCheck.CheckHealthAsync(checkContext); Assert.Equal(expected, healthCheckResult.Status); + Assert.NotEmpty(healthCheckResult.Data); if (healthCheckResult.Status != HealthStatus.Healthy) { - Assert.Equal("Memory" + expectedDescription, healthCheckResult.Description); + Assert.Equal("CPU and Memory" + expectedDescription, healthCheckResult.Description); } }