diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs
index f1a41ea303..1b08acb60b 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/DefaultHealthCheckService.cs
@@ -73,30 +73,40 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
try
{
var result = await healthCheck.CheckHealthAsync(context, cancellationToken);
+ var duration = stopwatch.GetElapsedTime();
entry = new HealthReportEntry(
- result.Result ? HealthStatus.Healthy : registration.FailureStatus,
- result.Description,
- result.Exception,
- result.Data);
+ status: result.Result ? HealthStatus.Healthy : registration.FailureStatus,
+ description: result.Description,
+ duration: duration,
+ exception: result.Exception,
+ data: result.Data);
- Log.HealthCheckEnd(_logger, registration, entry, stopwatch.GetElapsedTime());
+ Log.HealthCheckEnd(_logger, registration, entry, duration);
Log.HealthCheckData(_logger, registration, entry);
}
// Allow cancellation to propagate.
catch (Exception ex) when (ex as OperationCanceledException == null)
{
- entry = new HealthReportEntry(HealthStatus.Failed, ex.Message, ex, data: null);
- Log.HealthCheckError(_logger, registration, ex, stopwatch.GetElapsedTime());
+ var duration = stopwatch.GetElapsedTime();
+ entry = new HealthReportEntry(
+ status: HealthStatus.Failed,
+ description: ex.Message,
+ duration: duration,
+ exception: ex,
+ data: null);
+
+ Log.HealthCheckError(_logger, registration, ex, duration);
}
entries[registration.Name] = entry;
}
}
- var report = new HealthReport(entries);
- Log.HealthCheckProcessingEnd(_logger, report.Status, totalTime.GetElapsedTime());
+ var totalElapsedTime = totalTime.GetElapsedTime();
+ var report = new HealthReport(entries, totalElapsedTime);
+ Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime);
return report;
}
}
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReport.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReport.cs
index 29359a547f..a0e6fac1cd 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReport.cs
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReport.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
@@ -14,10 +15,12 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// Create a new from the specified results.
///
/// A containing the results from each health check.
- public HealthReport(IReadOnlyDictionary entries)
+ /// A value indicating the time the health check service took to execute.
+ public HealthReport(IReadOnlyDictionary entries, TimeSpan totalDuration)
{
Entries = entries;
Status = CalculateAggregateStatus(entries.Values);
+ TotalDuration = totalDuration;
}
///
@@ -35,6 +38,11 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
///
public HealthStatus Status { get; }
+ ///
+ /// Gets the time the health check service took to execute.
+ ///
+ public TimeSpan TotalDuration { get; }
+
private HealthStatus CalculateAggregateStatus(IEnumerable entries)
{
// This is basically a Min() check, but we know the possible range, so we don't need to walk the whole list
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReportEntry.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReportEntry.cs
index 17ed5ae288..2898c93aa4 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReportEntry.cs
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthReportEntry.cs
@@ -19,12 +19,14 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
///
/// A value indicating the health status of the component that was checked.
/// A human-readable description of the status of the component that was checked.
+ /// A value indicating the health execution duration.
/// An representing the exception that was thrown when checking for status (if any).
/// Additional key-value pairs describing the health of the component.
- public HealthReportEntry(HealthStatus status, string description, Exception exception, IReadOnlyDictionary data)
+ public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary data)
{
Status = status;
Description = description;
+ Duration = duration;
Exception = exception;
Data = data ?? _emptyReadOnlyDictionary;
}
@@ -39,6 +41,11 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
///
public string Description { get; }
+ ///
+ /// Gets the health check execution duration.
+ ///
+ public TimeSpan Duration { get; }
+
///
/// Gets an representing the exception that was thrown when checking for status (if any).
///
diff --git a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs
index 9ce0a4b620..453398e639 100644
--- a/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs
+++ b/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests/HealthReportTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
using Xunit;
@@ -17,15 +18,29 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
var result = new HealthReport(new Dictionary()
{
- {"Foo", new HealthReportEntry(HealthStatus.Healthy, null, null, null) },
- {"Bar", new HealthReportEntry(HealthStatus.Healthy, null, null, null) },
- {"Baz", new HealthReportEntry(status, exception: null, description: null, data: null) },
- {"Quick", new HealthReportEntry(HealthStatus.Healthy, null, null, null) },
- {"Quack", new HealthReportEntry(HealthStatus.Healthy, null, null, null) },
- {"Quock", new HealthReportEntry(HealthStatus.Healthy, null, null, null) },
- });
+ {"Foo", new HealthReportEntry(HealthStatus.Healthy, null,TimeSpan.MinValue, null, null) },
+ {"Bar", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue,null, null) },
+ {"Baz", new HealthReportEntry(status, exception: null, description: null,duration:TimeSpan.MinValue, data: null) },
+ {"Quick", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) },
+ {"Quack", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) },
+ {"Quock", new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.MinValue, null, null) },
+ }, totalDuration: TimeSpan.MinValue);
Assert.Equal(status, result.Status);
}
+
+ [Theory]
+ [InlineData(200)]
+ [InlineData(300)]
+ [InlineData(400)]
+ public void TotalDuration_MatchesTotalDurationParameter(int milliseconds)
+ {
+ var result = new HealthReport(new Dictionary()
+ {
+ {"Foo", new HealthReportEntry(HealthStatus.Healthy, null,TimeSpan.MinValue, null, null) }
+ }, totalDuration: TimeSpan.FromMilliseconds(milliseconds));
+
+ Assert.Equal(TimeSpan.FromMilliseconds(milliseconds), result.TotalDuration);
+ }
}
}