2020-12-05 09:54:59 +01:00
|
|
|
// Copyright (c) Umbraco.
|
|
|
|
|
// See LICENSE for more details.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-10-30 13:56:13 +01:00
|
|
|
using System.Linq;
|
2020-11-02 11:59:39 +01:00
|
|
|
using System.Threading.Tasks;
|
2020-10-30 13:56:13 +01:00
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Microsoft.Extensions.Options;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core;
|
|
|
|
|
using Umbraco.Cms.Core.Configuration;
|
|
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
|
|
|
using Umbraco.Cms.Core.HealthChecks;
|
|
|
|
|
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
|
|
|
|
|
using Umbraco.Cms.Core.Logging;
|
|
|
|
|
using Umbraco.Cms.Core.Runtime;
|
2021-02-15 11:41:12 +01:00
|
|
|
using Umbraco.Cms.Core.Scoping;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Services;
|
|
|
|
|
using Umbraco.Cms.Core.Sync;
|
2021-02-09 13:32:34 +01:00
|
|
|
using Umbraco.Extensions;
|
2017-09-08 19:39:13 +02:00
|
|
|
|
2021-02-12 11:03:28 +01:00
|
|
|
namespace Umbraco.Cms.Infrastructure.HostedServices
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-10-30 13:56:13 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Hosted service implementation for recurring health check notifications.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class HealthCheckNotifier : RecurringHostedServiceBase
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-10-30 13:56:13 +01:00
|
|
|
private readonly HealthChecksSettings _healthChecksSettings;
|
2017-09-13 17:35:20 +02:00
|
|
|
private readonly HealthCheckCollection _healthChecks;
|
|
|
|
|
private readonly HealthCheckNotificationMethodCollection _notifications;
|
2020-10-30 13:56:13 +01:00
|
|
|
private readonly IRuntimeState _runtimeState;
|
2020-12-24 16:35:59 +11:00
|
|
|
private readonly IServerRoleAccessor _serverRegistrar;
|
2020-10-30 13:56:13 +01:00
|
|
|
private readonly IMainDom _mainDom;
|
2020-07-08 17:26:31 +10:00
|
|
|
private readonly IScopeProvider _scopeProvider;
|
2020-09-28 08:26:21 +02:00
|
|
|
private readonly ILogger<HealthCheckNotifier> _logger;
|
2020-10-30 13:56:13 +01:00
|
|
|
private readonly IProfilingLogger _profilingLogger;
|
2017-09-08 19:39:13 +02:00
|
|
|
|
2020-12-05 09:54:59 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="HealthCheckNotifier"/> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="healthChecksSettings">The configuration for health check settings.</param>
|
|
|
|
|
/// <param name="healthChecks">The collection of healthchecks.</param>
|
|
|
|
|
/// <param name="notifications">The collection of healthcheck notification methods.</param>
|
|
|
|
|
/// <param name="runtimeState">Representation of the state of the Umbraco runtime.</param>
|
|
|
|
|
/// <param name="serverRegistrar">Provider of server registrations to the distributed cache.</param>
|
|
|
|
|
/// <param name="mainDom">Representation of the main application domain.</param>
|
|
|
|
|
/// <param name="scopeProvider">Provides scopes for database operations.</param>
|
|
|
|
|
/// <param name="logger">The typed logger.</param>
|
|
|
|
|
/// <param name="profilingLogger">The profiling logger.</param>
|
|
|
|
|
/// <param name="cronTabParser">Parser of crontab expressions.</param>
|
2020-08-06 12:59:21 +02:00
|
|
|
public HealthCheckNotifier(
|
2020-10-30 13:56:13 +01:00
|
|
|
IOptions<HealthChecksSettings> healthChecksSettings,
|
2020-08-06 12:59:21 +02:00
|
|
|
HealthCheckCollection healthChecks,
|
|
|
|
|
HealthCheckNotificationMethodCollection notifications,
|
2020-10-30 13:56:13 +01:00
|
|
|
IRuntimeState runtimeState,
|
2020-12-24 16:35:59 +11:00
|
|
|
IServerRoleAccessor serverRegistrar,
|
2020-08-06 12:59:21 +02:00
|
|
|
IMainDom mainDom,
|
2020-10-30 13:56:13 +01:00
|
|
|
IScopeProvider scopeProvider,
|
2020-09-28 08:26:21 +02:00
|
|
|
ILogger<HealthCheckNotifier> logger,
|
2020-11-03 09:02:15 +01:00
|
|
|
IProfilingLogger profilingLogger,
|
|
|
|
|
ICronTabParser cronTabParser)
|
2020-12-05 09:54:59 +01:00
|
|
|
: base(
|
|
|
|
|
healthChecksSettings.Value.Notification.Period,
|
|
|
|
|
healthChecksSettings.Value.GetNotificationDelay(cronTabParser, DateTime.Now, DefaultDelay))
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-10-30 13:56:13 +01:00
|
|
|
_healthChecksSettings = healthChecksSettings.Value;
|
2017-09-13 17:35:20 +02:00
|
|
|
_healthChecks = healthChecks;
|
|
|
|
|
_notifications = notifications;
|
2020-10-30 13:56:13 +01:00
|
|
|
_runtimeState = runtimeState;
|
|
|
|
|
_serverRegistrar = serverRegistrar;
|
2020-03-25 15:06:22 +11:00
|
|
|
_mainDom = mainDom;
|
2020-07-08 17:26:31 +10:00
|
|
|
_scopeProvider = scopeProvider;
|
2017-09-13 17:35:20 +02:00
|
|
|
_logger = logger;
|
2020-10-30 13:56:13 +01:00
|
|
|
_profilingLogger = profilingLogger;
|
2017-09-08 19:39:13 +02:00
|
|
|
}
|
2020-11-03 09:02:15 +01:00
|
|
|
|
2021-03-15 07:49:33 +01:00
|
|
|
public override async Task PerformExecuteAsync(object state)
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-10-30 13:56:13 +01:00
|
|
|
if (_healthChecksSettings.Notification.Enabled == false)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-13 17:35:20 +02:00
|
|
|
if (_runtimeState.Level != RuntimeLevel.Run)
|
2020-10-30 13:56:13 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-08 19:39:13 +02:00
|
|
|
|
2020-12-24 16:35:59 +11:00
|
|
|
switch (_serverRegistrar.CurrentServerRole)
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2018-09-10 11:32:49 +10:00
|
|
|
case ServerRole.Replica:
|
2020-09-15 12:40:35 +02:00
|
|
|
_logger.LogDebug("Does not run on replica servers.");
|
2020-10-30 13:56:13 +01:00
|
|
|
return;
|
2017-09-08 19:39:13 +02:00
|
|
|
case ServerRole.Unknown:
|
2020-09-15 12:40:35 +02:00
|
|
|
_logger.LogDebug("Does not run on servers with unknown role.");
|
2020-10-30 13:56:13 +01:00
|
|
|
return;
|
2017-09-08 19:39:13 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:56:13 +01:00
|
|
|
// Ensure we do not run if not main domain, but do NOT lock it
|
2020-03-25 15:06:22 +11:00
|
|
|
if (_mainDom.IsMainDom == false)
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-09-15 12:40:35 +02:00
|
|
|
_logger.LogDebug("Does not run if not MainDom.");
|
2020-10-30 13:56:13 +01:00
|
|
|
return;
|
2017-09-08 19:39:13 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-08 17:26:31 +10:00
|
|
|
// Ensure we use an explicit scope since we are running on a background thread and plugin health
|
|
|
|
|
// checks can be making service/database calls so we want to ensure the CallContext/Ambient scope
|
|
|
|
|
// isn't used since that can be problematic.
|
2020-12-05 09:54:59 +01:00
|
|
|
using (IScope scope = _scopeProvider.CreateScope())
|
2020-09-29 10:42:06 +02:00
|
|
|
using (_profilingLogger.DebugDuration<HealthCheckNotifier>("Health checks executing", "Health checks complete"))
|
2017-09-08 19:39:13 +02:00
|
|
|
{
|
2020-10-30 13:56:13 +01:00
|
|
|
// Don't notify for any checks that are disabled, nor for any disabled just for notifications.
|
2020-12-05 09:54:59 +01:00
|
|
|
Guid[] disabledCheckIds = _healthChecksSettings.Notification.DisabledChecks
|
2017-09-08 19:39:13 +02:00
|
|
|
.Select(x => x.Id)
|
2020-10-30 13:56:13 +01:00
|
|
|
.Union(_healthChecksSettings.DisabledChecks
|
2017-09-08 19:39:13 +02:00
|
|
|
.Select(x => x.Id))
|
|
|
|
|
.Distinct()
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
2021-02-03 07:42:56 +01:00
|
|
|
IEnumerable<HealthCheck> checks = _healthChecks
|
2017-09-08 19:39:13 +02:00
|
|
|
.Where(x => disabledCheckIds.Contains(x.Id) == false);
|
|
|
|
|
|
2021-02-03 07:42:56 +01:00
|
|
|
var results = await HealthCheckResults.Create(checks);
|
2017-09-08 19:39:13 +02:00
|
|
|
results.LogResults();
|
|
|
|
|
|
2020-10-30 13:56:13 +01:00
|
|
|
// Send using registered notification methods that are enabled.
|
2020-12-05 09:54:59 +01:00
|
|
|
foreach (IHealthCheckNotificationMethod notificationMethod in _notifications.Where(x => x.Enabled))
|
2020-10-30 13:56:13 +01:00
|
|
|
{
|
|
|
|
|
await notificationMethod.SendAsync(results);
|
|
|
|
|
}
|
2017-09-08 19:39:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-23 10:08:18 +02:00
|
|
|
}
|