File: TelemetryHealthCheckPublisher.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.HealthChecks.Common\Microsoft.Extensions.Diagnostics.HealthChecks.Common.csproj (Microsoft.Extensions.Diagnostics.HealthChecks.Common)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Diagnostics;
using Microsoft.Shared.Pools;
 
namespace Microsoft.Extensions.Diagnostics.HealthChecks;
 
/// <summary>
/// Performs metering and logging telemetry on the HealthReport.
/// </summary>
internal sealed class TelemetryHealthCheckPublisher : IHealthCheckPublisher
{
    private readonly HealthCheckMetrics _metrics;
    private readonly ILogger _logger;
    private readonly bool _logOnlyUnhealthy;
 
    /// <summary>
    /// Initializes a new instance of the <see cref="TelemetryHealthCheckPublisher"/> class.
    /// </summary>
    /// <param name="metrics">The metrics.</param>
    /// <param name="logger">The logger.</param>
    /// <param name="options">Creation options.</param>
    public TelemetryHealthCheckPublisher(HealthCheckMetrics metrics, ILogger<TelemetryHealthCheckPublisher> logger, IOptions<TelemetryHealthCheckPublisherOptions> options)
    {
        var value = Throw.IfMemberNull(options, options.Value);
        _logOnlyUnhealthy = Throw.IfMemberNull(options, options.Value.LogOnlyUnhealthy);
        _metrics = metrics;
        _logger = logger;
    }
 
    /// <summary>
    /// Performs logging and metering before publishing the provided report.
    /// </summary>
    /// <param name="report">The <see cref="HealthReport"/>. The result of executing a set of health checks.</param>
    /// <param name="cancellationToken">Not used in the current implementation.</param>
    /// <returns>Task.CompletedTask.</returns>
    public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
    {
        _ = Throw.IfNull(report);
 
        if (report.Status == HealthStatus.Healthy)
        {
            if (!_logOnlyUnhealthy)
            {
                Log.Healthy(_logger, report.Status);
            }
        }
        else
        {
            var stringBuilder = PoolFactory.SharedStringBuilderPool.Get();
 
            // Construct string showing list of all health entries status and description for logs
            string separator = string.Empty;
            foreach (var entry in report.Entries)
            {
                if (entry.Value.Status != HealthStatus.Healthy)
                {
                    _metrics.UnhealthyHealthCheckCounter.RecordMetric(entry.Key, entry.Value.Status);
                }
 
                _ = stringBuilder.Append(separator)
                    .Append(entry.Key)
                    .Append(": {")
                    .Append("status: ")
                    .Append(entry.Value.Status.ToString())
                    .Append(", description: ")
                    .Append(entry.Value.Description)
                    .Append('}');
 
                separator = ", ";
            }
 
            Log.Unhealthy(_logger, report.Status, stringBuilder);
            PoolFactory.SharedStringBuilderPool.Return(stringBuilder);
        }
 
        _metrics.HealthCheckReportCounter.RecordMetric(report.Status);
 
        return Task.CompletedTask;
    }
}