Integrate WebStatus with Healthcheckpull/899/head
@ -1,76 +0,0 @@ | |||
// 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.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.AspNetCore.Http.Features; | |||
using Microsoft.Extensions.HealthChecks; | |||
using Newtonsoft.Json; | |||
namespace Microsoft.AspNetCore.HealthChecks | |||
{ | |||
public class HealthCheckMiddleware | |||
{ | |||
private readonly RequestDelegate _next; | |||
private readonly string _path; | |||
private readonly int? _port; | |||
private readonly IHealthCheckService _service; | |||
private readonly TimeSpan _timeout; | |||
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port, TimeSpan timeout) | |||
{ | |||
_port = port; | |||
_service = service; | |||
_next = next; | |||
_timeout = timeout; | |||
} | |||
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path, TimeSpan timeout) | |||
{ | |||
_path = path; | |||
_service = service; | |||
_next = next; | |||
_timeout = timeout; | |||
} | |||
public async Task Invoke(HttpContext context) | |||
{ | |||
if (IsHealthCheckRequest(context)) | |||
{ | |||
var timeoutTokenSource = new CancellationTokenSource(_timeout); | |||
var result = await _service.CheckHealthAsync(timeoutTokenSource.Token); | |||
var status = result.CheckStatus; | |||
if (status != CheckStatus.Healthy) | |||
context.Response.StatusCode = 503; | |||
context.Response.Headers.Add("content-type", "application/json"); | |||
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = status.ToString() })); | |||
return; | |||
} | |||
else | |||
{ | |||
await _next.Invoke(context); | |||
} | |||
} | |||
private bool IsHealthCheckRequest(HttpContext context) | |||
{ | |||
if (_port.HasValue) | |||
{ | |||
var connInfo = context.Features.Get<IHttpConnectionFeature>(); | |||
if (connInfo.LocalPort == _port) | |||
return true; | |||
} | |||
if (context.Request.Path == _path) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
} | |||
} |
@ -1,45 +0,0 @@ | |||
// 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 Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
namespace Microsoft.AspNetCore.HealthChecks | |||
{ | |||
public class HealthCheckStartupFilter : IStartupFilter | |||
{ | |||
private string _path; | |||
private int? _port; | |||
private TimeSpan _timeout; | |||
public HealthCheckStartupFilter(int port, TimeSpan timeout) | |||
{ | |||
_port = port; | |||
_timeout = timeout; | |||
} | |||
public HealthCheckStartupFilter(string path, TimeSpan timeout) | |||
{ | |||
_path = path; | |||
_timeout = timeout; | |||
} | |||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) | |||
{ | |||
return app => | |||
{ | |||
if (_port.HasValue) | |||
{ | |||
app.UseMiddleware<HealthCheckMiddleware>(_port, _timeout); | |||
} | |||
else | |||
{ | |||
app.UseMiddleware<HealthCheckMiddleware>(_path, _timeout); | |||
} | |||
next(app); | |||
}; | |||
} | |||
} | |||
} |
@ -1,47 +0,0 @@ | |||
// 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 Microsoft.AspNetCore.HealthChecks; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace Microsoft.AspNetCore.Hosting | |||
{ | |||
public static class HealthCheckWebHostBuilderExtension | |||
{ | |||
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); | |||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port) | |||
=> UseHealthChecks(builder, port, DefaultTimeout); | |||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port, TimeSpan timeout) | |||
{ | |||
Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535."); | |||
Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span."); | |||
builder.ConfigureServices(services => | |||
{ | |||
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey); | |||
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}"); | |||
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port, timeout)); | |||
}); | |||
return builder; | |||
} | |||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path) | |||
=> UseHealthChecks(builder, path, DefaultTimeout); | |||
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path, TimeSpan timeout) | |||
{ | |||
Guard.ArgumentNotNull(nameof(path), path); | |||
// REVIEW: Is there a better URL path validator somewhere? | |||
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values."); | |||
Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with '/'."); | |||
Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span."); | |||
builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path, timeout))); | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,38 +0,0 @@ | |||
// 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 Microsoft.Extensions.HealthChecks; | |||
namespace Microsoft.AspNetCore.Hosting | |||
{ | |||
public static class HealthCheckWebHostExtensions | |||
{ | |||
private const int DEFAULT_TIMEOUT_SECONDS = 300; | |||
public static void RunWhenHealthy(this IWebHost webHost) | |||
{ | |||
webHost.RunWhenHealthy(TimeSpan.FromSeconds(DEFAULT_TIMEOUT_SECONDS)); | |||
} | |||
public static void RunWhenHealthy(this IWebHost webHost, TimeSpan timeout) | |||
{ | |||
var healthChecks = webHost.Services.GetService(typeof(IHealthCheckService)) as IHealthCheckService; | |||
var loops = 0; | |||
do | |||
{ | |||
var checkResult = healthChecks.CheckHealthAsync().Result; | |||
if (checkResult.CheckStatus == CheckStatus.Healthy) | |||
{ | |||
webHost.Run(); | |||
break; | |||
} | |||
System.Threading.Thread.Sleep(1000); | |||
loops++; | |||
} while (loops < timeout.TotalSeconds); | |||
} | |||
} | |||
} |
@ -1,19 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0-preview3-35497" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,175 +0,0 @@ | |||
// 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 Microsoft.WindowsAzure.Storage; | |||
using Microsoft.WindowsAzure.Storage.Auth; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
// REVIEW: Do we want these to continue to use default parameters? | |||
// REVIEW: What are the appropriate guards for these functions? | |||
public static class AzureHealthCheckBuilderExtensions | |||
{ | |||
public static HealthCheckBuilder AddAzureBlobStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string containerName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
var credentials = new StorageCredentials(accountName, accountKey); | |||
var storageAccount = new CloudStorageAccount(credentials, true); | |||
return AddAzureBlobStorageCheck(builder, storageAccount, containerName, cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddAzureBlobStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string containerName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
builder.AddCheck($"AzureBlobStorageCheck {storageAccount.BlobStorageUri} {containerName}", async () => | |||
{ | |||
bool result; | |||
try | |||
{ | |||
var blobClient = storageAccount.CreateCloudBlobClient(); | |||
var properties = await blobClient.GetServicePropertiesAsync().ConfigureAwait(false); | |||
if (!String.IsNullOrWhiteSpace(containerName)) | |||
{ | |||
var container = blobClient.GetContainerReference(containerName); | |||
result = await container.ExistsAsync(); | |||
} | |||
result = true; | |||
} | |||
catch (Exception) | |||
{ | |||
result = false; | |||
} | |||
return result | |||
? HealthCheckResult.Healthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is available") | |||
: HealthCheckResult.Unhealthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is unavailable"); | |||
}, cacheDuration ?? builder.DefaultCacheDuration); | |||
return builder; | |||
} | |||
public static HealthCheckBuilder AddAzureTableStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string tableName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
var credentials = new StorageCredentials(accountName, accountKey); | |||
var storageAccount = new CloudStorageAccount(credentials, true); | |||
return AddAzureTableStorageCheck(builder, storageAccount, tableName, cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddAzureTableStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string tableName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
builder.AddCheck($"AzureTableStorageCheck {storageAccount.TableStorageUri} {tableName}", async () => | |||
{ | |||
bool result; | |||
try | |||
{ | |||
var tableClient = storageAccount.CreateCloudTableClient(); | |||
var properties = await tableClient.GetServicePropertiesAsync().ConfigureAwait(false); | |||
if (!String.IsNullOrWhiteSpace(tableName)) | |||
{ | |||
var table = tableClient.GetTableReference(tableName); | |||
result = await table.ExistsAsync(); | |||
} | |||
result = true; | |||
} | |||
catch (Exception) | |||
{ | |||
result = false; | |||
} | |||
return result | |||
? HealthCheckResult.Healthy($"AzureTableStorage {storageAccount.BlobStorageUri} is available") | |||
: HealthCheckResult.Unhealthy($"AzureTableStorage {storageAccount.BlobStorageUri} is unavailable"); | |||
}, cacheDuration ?? builder.DefaultCacheDuration); | |||
return builder; | |||
} | |||
public static HealthCheckBuilder AddAzureFileStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string shareName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
var credentials = new StorageCredentials(accountName, accountKey); | |||
var storageAccount = new CloudStorageAccount(credentials, true); | |||
return AddAzureFileStorageCheck(builder, storageAccount, shareName, cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddAzureFileStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string shareName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
builder.AddCheck($"AzureFileStorageCheck {storageAccount.FileStorageUri} {shareName}", async () => | |||
{ | |||
bool result; | |||
try | |||
{ | |||
var fileClient = storageAccount.CreateCloudFileClient(); | |||
var properties = await fileClient.GetServicePropertiesAsync().ConfigureAwait(false); | |||
if (!String.IsNullOrWhiteSpace(shareName)) | |||
{ | |||
var share = fileClient.GetShareReference(shareName); | |||
result = await share.ExistsAsync(); | |||
} | |||
result = true; | |||
} | |||
catch (Exception) | |||
{ | |||
result = false; | |||
} | |||
return result | |||
? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available") | |||
: HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable"); | |||
}, cacheDuration ?? builder.DefaultCacheDuration); | |||
return builder; | |||
} | |||
public static HealthCheckBuilder AddAzureQueueStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string queueName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
var credentials = new StorageCredentials(accountName, accountKey); | |||
var storageAccount = new CloudStorageAccount(credentials, true); | |||
return AddAzureQueueStorageCheck(builder, storageAccount, queueName, cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddAzureQueueStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string queueName = null, TimeSpan? cacheDuration = null) | |||
{ | |||
builder.AddCheck($"AzureQueueStorageCheck {storageAccount.QueueStorageUri} {queueName}", async () => | |||
{ | |||
bool result; | |||
try | |||
{ | |||
var queueClient = storageAccount.CreateCloudQueueClient(); | |||
var properties = await queueClient.GetServicePropertiesAsync().ConfigureAwait(false); | |||
if (!String.IsNullOrWhiteSpace(queueName)) | |||
{ | |||
var queue = queueClient.GetQueueReference(queueName); | |||
result = await queue.ExistsAsync(); | |||
} | |||
result = true; | |||
} | |||
catch (Exception) | |||
{ | |||
result = false; | |||
} | |||
return result | |||
? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available") | |||
: HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable"); | |||
}, cacheDuration ?? builder.DefaultCacheDuration); | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,26 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> | |||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> | |||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0-preview3-35497" /> | |||
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" /> | |||
<PackageReference Include="WindowsAzure.Storage" Version="9.1.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,22 +0,0 @@ | |||
// 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.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
// General Information about an assembly is controlled through the following | |||
// set of attributes. Change these attribute values to modify the information | |||
// associated with an assembly. | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("HealthChecks.Azure")] | |||
[assembly: AssemblyTrademark("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("0c4158b7-7153-4d2e-abfa-4ce07d44f75f")] |
@ -1,54 +0,0 @@ | |||
// 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.Data; | |||
using System.Data.SqlClient; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
// REVIEW: What are the appropriate guards for these functions? | |||
public static class HealthCheckBuilderSqlServerExtensions | |||
{ | |||
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddSqlCheck(builder, name, connectionString, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString, TimeSpan cacheDuration) | |||
{ | |||
builder.AddCheck($"SqlCheck({name})", async () => | |||
{ | |||
try | |||
{ | |||
//TODO: There is probably a much better way to do this. | |||
using (var connection = new SqlConnection(connectionString)) | |||
{ | |||
connection.Open(); | |||
using (var command = connection.CreateCommand()) | |||
{ | |||
command.CommandType = CommandType.Text; | |||
command.CommandText = "SELECT 1"; | |||
var result = (int)await command.ExecuteScalarAsync().ConfigureAwait(false); | |||
if (result == 1) | |||
{ | |||
return HealthCheckResult.Healthy($"SqlCheck({name}): Healthy"); | |||
} | |||
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Unhealthy"); | |||
} | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}"); | |||
} | |||
}, cacheDuration); | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,19 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="System.Data.SqlClient" Version="4.6.0-preview3-27014-02" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,109 +0,0 @@ | |||
// 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.Reflection; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public abstract class CachedHealthCheck | |||
{ | |||
private static readonly TypeInfo HealthCheckTypeInfo = typeof(IHealthCheck).GetTypeInfo(); | |||
private volatile int _writerCount; | |||
public CachedHealthCheck(string name, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(name), name); | |||
Guard.ArgumentValid(cacheDuration.TotalMilliseconds >= 0, nameof(cacheDuration), "Cache duration must be zero (disabled) or greater than zero."); | |||
Name = name; | |||
CacheDuration = cacheDuration; | |||
} | |||
public IHealthCheckResult CachedResult { get; internal set; } | |||
public TimeSpan CacheDuration { get; } | |||
public DateTimeOffset CacheExpiration { get; internal set; } | |||
public string Name { get; } | |||
protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow; | |||
protected abstract IHealthCheck Resolve(IServiceProvider serviceProvider); | |||
public async ValueTask<IHealthCheckResult> RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
while (CacheExpiration <= UtcNow) | |||
{ | |||
// Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value, | |||
// and the waiters who aren't allowed to write will just spin wait for the new value. | |||
if (Interlocked.Exchange(ref _writerCount, 1) != 0) | |||
{ | |||
await Task.Delay(5, cancellationToken).ConfigureAwait(false); | |||
continue; | |||
} | |||
try | |||
{ | |||
var check = Resolve(serviceProvider); | |||
CachedResult = await check.CheckAsync(cancellationToken); | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
CachedResult = HealthCheckResult.Unhealthy("The health check operation timed out"); | |||
} | |||
catch (Exception ex) | |||
{ | |||
CachedResult = HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}"); | |||
} | |||
CacheExpiration = UtcNow + CacheDuration; | |||
_writerCount = 0; | |||
break; | |||
} | |||
return CachedResult; | |||
} | |||
public static CachedHealthCheck FromHealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) | |||
{ | |||
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck); | |||
return new TypeOrHealthCheck_HealthCheck(name, cacheDuration, healthCheck); | |||
} | |||
public static CachedHealthCheck FromType(string name, TimeSpan cacheDuration, Type healthCheckType) | |||
{ | |||
Guard.ArgumentNotNull(nameof(healthCheckType), healthCheckType); | |||
Guard.ArgumentValid(HealthCheckTypeInfo.IsAssignableFrom(healthCheckType.GetTypeInfo()), nameof(healthCheckType), $"Health check must implement '{typeof(IHealthCheck).FullName}'."); | |||
return new TypeOrHealthCheck_Type(name, cacheDuration, healthCheckType); | |||
} | |||
class TypeOrHealthCheck_HealthCheck : CachedHealthCheck | |||
{ | |||
private readonly IHealthCheck _healthCheck; | |||
public TypeOrHealthCheck_HealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) : base(name, cacheDuration) | |||
=> _healthCheck = healthCheck; | |||
protected override IHealthCheck Resolve(IServiceProvider serviceProvider) => _healthCheck; | |||
} | |||
class TypeOrHealthCheck_Type : CachedHealthCheck | |||
{ | |||
private readonly Type _healthCheckType; | |||
public TypeOrHealthCheck_Type(string name, TimeSpan cacheDuration, Type healthCheckType) : base(name, cacheDuration) | |||
=> _healthCheckType = healthCheckType; | |||
protected override IHealthCheck Resolve(IServiceProvider serviceProvider) | |||
=> (IHealthCheck)serviceProvider.GetRequiredService(_healthCheckType); | |||
} | |||
} | |||
} |
@ -1,19 +0,0 @@ | |||
// 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.Threading; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public static class CachedHealthCheckExtensions | |||
{ | |||
public static ValueTask<IHealthCheckResult> RunAsync(this CachedHealthCheck check, IServiceProvider serviceProvider) | |||
{ | |||
Guard.ArgumentNotNull(nameof(check), check); | |||
return check.RunAsync(serviceProvider, CancellationToken.None); | |||
} | |||
} | |||
} |
@ -1,13 +0,0 @@ | |||
// 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. | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public enum CheckStatus | |||
{ | |||
Unknown, | |||
Unhealthy, | |||
Healthy, | |||
Warning | |||
} | |||
} |
@ -1,116 +0,0 @@ | |||
// 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.Threading; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public static partial class HealthCheckBuilderExtensions | |||
{ | |||
// Lambda versions of AddCheck for Func/Func<Task>/Func<ValueTask> | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration); | |||
} | |||
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration); | |||
} | |||
// IHealthCheck versions of AddCheck | |||
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string checkName, IHealthCheck check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck(checkName, check, builder.DefaultCacheDuration); | |||
} | |||
// Type versions of AddCheck | |||
public static HealthCheckBuilder AddCheck<TCheck>(this HealthCheckBuilder builder, string name) where TCheck : class, IHealthCheck | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return builder.AddCheck<TCheck>(name, builder.DefaultCacheDuration); | |||
} | |||
} | |||
} |
@ -1,69 +0,0 @@ | |||
// 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.HealthChecks | |||
{ | |||
public static partial class HealthCheckBuilderExtensions | |||
{ | |||
// Numeric checks | |||
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc) where T : IComparable<T> | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddMinValueCheck(builder, name, minValue, currentValueFunc, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc, TimeSpan cacheDuration) | |||
where T : IComparable<T> | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
Guard.ArgumentNotNullOrEmpty(nameof(name), name); | |||
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc); | |||
builder.AddCheck(name, () => | |||
{ | |||
var currentValue = currentValueFunc(); | |||
var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy; | |||
return HealthCheckResult.FromStatus( | |||
status, | |||
$"min={minValue}, current={currentValue}", | |||
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } } | |||
); | |||
}, cacheDuration); | |||
return builder; | |||
} | |||
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc) where T : IComparable<T> | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddMaxValueCheck(builder, name, maxValue, currentValueFunc, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc, TimeSpan cacheDuration) | |||
where T : IComparable<T> | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
Guard.ArgumentNotNullOrEmpty(nameof(name), name); | |||
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc); | |||
builder.AddCheck(name, () => | |||
{ | |||
var currentValue = currentValueFunc(); | |||
var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy; | |||
return HealthCheckResult.FromStatus( | |||
status, | |||
$"max={maxValue}, current={currentValue}", | |||
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } } | |||
); | |||
}, cacheDuration); | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,31 +0,0 @@ | |||
// 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.Diagnostics; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public static partial class HealthCheckBuilderExtensions | |||
{ | |||
// System checks | |||
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) | |||
=> AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64); | |||
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) | |||
=> AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64, cacheDuration); | |||
public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) | |||
=> AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64); | |||
public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) | |||
=> AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64, cacheDuration); | |||
public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize) | |||
=> AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64); | |||
public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) | |||
=> AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64, cacheDuration); | |||
} | |||
} |
@ -1,83 +0,0 @@ | |||
// 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.Net.Http; | |||
using System.Threading.Tasks; | |||
using Microsoft.Extensions.HealthChecks.Internal; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public static partial class HealthCheckBuilderExtensions | |||
{ | |||
// Default URL check | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddUrlCheck(builder, url, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration) | |||
=> AddUrlCheck(builder, url, response => UrlChecker.DefaultUrlCheck(response), cacheDuration); | |||
// Func returning IHealthCheckResult | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, IHealthCheckResult> checkFunc) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, | |||
Func<HttpResponseMessage, IHealthCheckResult> checkFunc, | |||
TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); | |||
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)), cacheDuration); | |||
} | |||
// Func returning Task<IHealthCheckResult> | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, | |||
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc, | |||
TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); | |||
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)), cacheDuration); | |||
} | |||
// Func returning ValueTask<IHealthCheckResult> | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); | |||
} | |||
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, | |||
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, | |||
TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNull(nameof(builder), builder); | |||
Guard.ArgumentNotNullOrEmpty(nameof(url), url); | |||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); | |||
var urlCheck = new UrlChecker(checkFunc, url); | |||
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync(), cacheDuration); | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,86 +0,0 @@ | |||
// 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 System.Linq; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
/// <summary> | |||
/// Represents a composite health check result built from several results. | |||
/// </summary> | |||
public class CompositeHealthCheckResult : IHealthCheckResult | |||
{ | |||
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>(); | |||
private readonly CheckStatus _initialStatus; | |||
private readonly CheckStatus _partiallyHealthyStatus; | |||
private readonly Dictionary<string, IHealthCheckResult> _results = new Dictionary<string, IHealthCheckResult>(StringComparer.OrdinalIgnoreCase); | |||
public CompositeHealthCheckResult(CheckStatus partiallyHealthyStatus = CheckStatus.Warning, | |||
CheckStatus initialStatus = CheckStatus.Unknown) | |||
{ | |||
_partiallyHealthyStatus = partiallyHealthyStatus; | |||
_initialStatus = initialStatus; | |||
} | |||
public CheckStatus CheckStatus | |||
{ | |||
get | |||
{ | |||
var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus)); | |||
if (checkStatuses.Count == 0) | |||
{ | |||
return _initialStatus; | |||
} | |||
if (checkStatuses.Count == 1) | |||
{ | |||
return checkStatuses.First(); | |||
} | |||
if (checkStatuses.Contains(CheckStatus.Healthy)) | |||
{ | |||
return _partiallyHealthyStatus; | |||
} | |||
return CheckStatus.Unhealthy; | |||
} | |||
} | |||
public string Description => string.Join(Environment.NewLine, _results.Select(r => $"{r.Key}: {r.Value.Description}")); | |||
public IReadOnlyDictionary<string, object> Data | |||
{ | |||
get | |||
{ | |||
var result = new Dictionary<string, object>(); | |||
foreach (var kvp in _results) | |||
result.Add(kvp.Key, kvp.Value.Data); | |||
return result; | |||
} | |||
} | |||
public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results; | |||
public void Add(string name, CheckStatus status, string description) | |||
=> Add(name, status, description, null); | |||
public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data) | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(name), name); | |||
Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add 'Unknown' status to composite health check result."); | |||
Guard.ArgumentNotNullOrEmpty(nameof(description), description); | |||
_results.Add(name, HealthCheckResult.FromStatus(status, description, data)); | |||
} | |||
public void Add(string name, IHealthCheckResult checkResult) | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(name), name); | |||
Guard.ArgumentNotNull(nameof(checkResult), checkResult); | |||
_results.Add(name, checkResult); | |||
} | |||
} | |||
} |
@ -1,42 +0,0 @@ | |||
// 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.Threading; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public class HealthCheck : IHealthCheck | |||
{ | |||
protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check) | |||
{ | |||
Guard.ArgumentNotNull(nameof(check), check); | |||
Check = check; | |||
} | |||
protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; } | |||
public ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken)) | |||
=> Check(cancellationToken); | |||
public static HealthCheck FromCheck(Func<IHealthCheckResult> check) | |||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check())); | |||
public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check) | |||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token))); | |||
public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check) | |||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check())); | |||
public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check) | |||
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token))); | |||
public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check) | |||
=> new HealthCheck(token => check()); | |||
public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check) | |||
=> new HealthCheck(check); | |||
} | |||
} |
@ -1,135 +0,0 @@ | |||
// 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.HealthChecks | |||
{ | |||
public class HealthCheckBuilder | |||
{ | |||
private readonly Dictionary<string, CachedHealthCheck> _checksByName; | |||
private readonly HealthCheckGroup _currentGroup; | |||
private readonly Dictionary<string, HealthCheckGroup> _groups; | |||
public HealthCheckBuilder() | |||
{ | |||
_checksByName = new Dictionary<string, CachedHealthCheck>(StringComparer.OrdinalIgnoreCase); | |||
_currentGroup = new HealthCheckGroup(string.Empty, CheckStatus.Unhealthy); | |||
_groups = new Dictionary<string, HealthCheckGroup>(StringComparer.OrdinalIgnoreCase) | |||
{ | |||
[string.Empty] = _currentGroup | |||
}; | |||
DefaultCacheDuration = TimeSpan.FromMinutes(5); | |||
} | |||
/// <summary> | |||
/// This constructor should only be used when creating a grouped health check builder. | |||
/// </summary> | |||
public HealthCheckBuilder(HealthCheckBuilder rootBuilder, HealthCheckGroup currentGroup) | |||
{ | |||
Guard.ArgumentNotNull(nameof(rootBuilder), rootBuilder); | |||
Guard.ArgumentNotNull(nameof(currentGroup), currentGroup); | |||
_checksByName = rootBuilder._checksByName; | |||
_currentGroup = currentGroup; | |||
_groups = rootBuilder._groups; | |||
DefaultCacheDuration = rootBuilder.DefaultCacheDuration; | |||
} | |||
/// <summary> | |||
/// Gets the registered checks, indexed by check name. | |||
/// </summary> | |||
public IReadOnlyDictionary<string, CachedHealthCheck> ChecksByName => _checksByName; | |||
/// <summary> | |||
/// Gets the current default cache duration used when registering checks. | |||
/// </summary> | |||
public TimeSpan DefaultCacheDuration { get; private set; } | |||
/// <summary> | |||
/// Gets the registered groups, indexed by group name. The root group's name is <see cref="string.Empty"/>. | |||
/// </summary> | |||
public IReadOnlyDictionary<string, HealthCheckGroup> Groups => _groups; | |||
/// <summary> | |||
/// Registers a health check type that will later be resolved via dependency | |||
/// injection. | |||
/// </summary> | |||
public HealthCheckBuilder AddCheck<TCheck>(string checkName, TimeSpan cacheDuration) where TCheck : class, IHealthCheck | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName); | |||
Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered."); | |||
var namedCheck = CachedHealthCheck.FromType(checkName, cacheDuration, typeof(TCheck)); | |||
_checksByName.Add(checkName, namedCheck); | |||
_currentGroup.ChecksInternal.Add(namedCheck); | |||
return this; | |||
} | |||
/// <summary> | |||
/// Registers a concrete health check to the builder. | |||
/// </summary> | |||
public HealthCheckBuilder AddCheck(string checkName, IHealthCheck check, TimeSpan cacheDuration) | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName); | |||
Guard.ArgumentNotNull(nameof(check), check); | |||
Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered."); | |||
var namedCheck = CachedHealthCheck.FromHealthCheck(checkName, cacheDuration, check); | |||
_checksByName.Add(checkName, namedCheck); | |||
_currentGroup.ChecksInternal.Add(namedCheck); | |||
return this; | |||
} | |||
/// <summary> | |||
/// Creates a new health check group, to which you can add one or more health | |||
/// checks. Uses <see cref="CheckStatus.Unhealthy"/> when the group is | |||
/// partially successful. | |||
/// </summary> | |||
public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks) | |||
=> AddHealthCheckGroup(groupName, groupChecks, CheckStatus.Unhealthy); | |||
/// <summary> | |||
/// Creates a new health check group, to which you can add one or more health | |||
/// checks. | |||
/// </summary> | |||
public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action<HealthCheckBuilder> groupChecks, CheckStatus partialSuccessStatus) | |||
{ | |||
Guard.ArgumentNotNullOrEmpty(nameof(groupName), groupName); | |||
Guard.ArgumentNotNull(nameof(groupChecks), groupChecks); | |||
Guard.ArgumentValid(partialSuccessStatus != CheckStatus.Unknown, nameof(partialSuccessStatus), "Check status 'Unknown' is not valid for partial success."); | |||
Guard.ArgumentValid(!_groups.ContainsKey(groupName), nameof(groupName), $"A group with name '{groupName}' has already been registered."); | |||
Guard.OperationValid(_currentGroup.GroupName == string.Empty, "Nested groups are not supported by HealthCheckBuilder."); | |||
var group = new HealthCheckGroup(groupName, partialSuccessStatus); | |||
_groups.Add(groupName, group); | |||
var innerBuilder = new HealthCheckBuilder(this, group); | |||
groupChecks(innerBuilder); | |||
return this; | |||
} | |||
public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration) | |||
{ | |||
Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration."); | |||
DefaultCacheDuration = duration; | |||
return this; | |||
} | |||
public HealthCheckBuilder WithPartialSuccessStatus(CheckStatus partiallyHealthyStatus) | |||
{ | |||
_currentGroup.PartiallyHealthyStatus = partiallyHealthyStatus; | |||
return this; | |||
} | |||
} | |||
} |
@ -1,37 +0,0 @@ | |||
// 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.Collections.Generic; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public class HealthCheckGroup | |||
{ | |||
private CheckStatus _partialSuccessStatus; | |||
public HealthCheckGroup(string groupName, CheckStatus partialSuccessStatus) | |||
{ | |||
Guard.ArgumentNotNull(nameof(groupName), groupName); | |||
GroupName = groupName; | |||
PartiallyHealthyStatus = partialSuccessStatus; | |||
} | |||
public IReadOnlyList<CachedHealthCheck> Checks => ChecksInternal.AsReadOnly(); | |||
internal List<CachedHealthCheck> ChecksInternal { get; } = new List<CachedHealthCheck>(); | |||
public string GroupName { get; } | |||
public CheckStatus PartiallyHealthyStatus | |||
{ | |||
get => _partialSuccessStatus; | |||
internal set | |||
{ | |||
Guard.ArgumentValid(value != CheckStatus.Unknown, nameof(value), "Check status 'Unknown' is not valid for partial success."); | |||
_partialSuccessStatus = value; | |||
} | |||
} | |||
} | |||
} |
@ -1,53 +0,0 @@ | |||
// 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.Collections.Generic; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public class HealthCheckResult : IHealthCheckResult | |||
{ | |||
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>(); | |||
public CheckStatus CheckStatus { get; } | |||
public IReadOnlyDictionary<string, object> Data { get; } | |||
public string Description { get; } | |||
private HealthCheckResult(CheckStatus checkStatus, string description, IReadOnlyDictionary<string, object> data) | |||
{ | |||
CheckStatus = checkStatus; | |||
Description = description; | |||
Data = data ?? _emptyData; | |||
} | |||
public static HealthCheckResult Unhealthy(string description) | |||
=> new HealthCheckResult(CheckStatus.Unhealthy, description, null); | |||
public static HealthCheckResult Unhealthy(string description, IReadOnlyDictionary<string, object> data) | |||
=> new HealthCheckResult(CheckStatus.Unhealthy, description, data); | |||
public static HealthCheckResult Healthy(string description) | |||
=> new HealthCheckResult(CheckStatus.Healthy, description, null); | |||
public static HealthCheckResult Healthy(string description, IReadOnlyDictionary<string, object> data) | |||
=> new HealthCheckResult(CheckStatus.Healthy, description, data); | |||
public static HealthCheckResult Warning(string description) | |||
=> new HealthCheckResult(CheckStatus.Warning, description, null); | |||
public static HealthCheckResult Warning(string description, IReadOnlyDictionary<string, object> data) | |||
=> new HealthCheckResult(CheckStatus.Warning, description, data); | |||
public static HealthCheckResult Unknown(string description) | |||
=> new HealthCheckResult(CheckStatus.Unknown, description, null); | |||
public static HealthCheckResult Unknown(string description, IReadOnlyDictionary<string, object> data) | |||
=> new HealthCheckResult(CheckStatus.Unknown, description, data); | |||
public static HealthCheckResult FromStatus(CheckStatus status, string description) | |||
=> new HealthCheckResult(status, description, null); | |||
public static HealthCheckResult FromStatus(CheckStatus status, string description, IReadOnlyDictionary<string, object> data) | |||
=> new HealthCheckResult(status, description, data); | |||
} | |||
} |
@ -1,12 +0,0 @@ | |||
// 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.Collections.Generic; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public class HealthCheckResults | |||
{ | |||
public IList<IHealthCheckResult> CheckResults { get; } = new List<IHealthCheckResult>(); | |||
} | |||
} |
@ -1,119 +0,0 @@ | |||
// 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 System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public class HealthCheckService : IHealthCheckService | |||
{ | |||
private readonly HealthCheckBuilder _builder; | |||
private readonly IReadOnlyList<HealthCheckGroup> _groups; | |||
private readonly HealthCheckGroup _root; | |||
private readonly IServiceProvider _serviceProvider; | |||
private readonly IServiceScopeFactory _serviceScopeFactory; | |||
public HealthCheckService(HealthCheckBuilder builder, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory) | |||
{ | |||
_builder = builder; | |||
_groups = GetGroups().Where(group => group.GroupName != string.Empty).ToList(); | |||
_root = GetGroup(string.Empty); | |||
_serviceProvider = serviceProvider; | |||
_serviceScopeFactory = serviceScopeFactory; | |||
} | |||
public async Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
using (var scope = GetServiceScope()) | |||
{ | |||
var scopeServiceProvider = scope.ServiceProvider; | |||
var groupTasks = _groups.Select(group => new { Group = group, Task = RunGroupAsync(scopeServiceProvider, group, cancellationToken) }).ToList(); | |||
var result = await RunGroupAsync(scopeServiceProvider, _root, cancellationToken).ConfigureAwait(false); | |||
await Task.WhenAll(groupTasks.Select(x => x.Task)); | |||
foreach (var groupTask in groupTasks) | |||
{ | |||
result.Add($"Group({groupTask.Group.GroupName})", groupTask.Task.Result); | |||
} | |||
return result; | |||
} | |||
} | |||
public IReadOnlyList<CachedHealthCheck> GetAllChecks() | |||
=> _builder.ChecksByName.Values.ToList().AsReadOnly(); | |||
public CachedHealthCheck GetCheck(string checkName) | |||
=> _builder.ChecksByName[checkName]; | |||
public HealthCheckGroup GetGroup(string groupName) | |||
=> _builder.Groups[groupName]; | |||
public IReadOnlyList<HealthCheckGroup> GetGroups() | |||
=> _builder.Groups.Values.ToList().AsReadOnly(); | |||
private IServiceScope GetServiceScope() | |||
=> _serviceScopeFactory == null ? new UnscopedServiceProvider(_serviceProvider) : _serviceScopeFactory.CreateScope(); | |||
public async ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
using (var scope = GetServiceScope()) | |||
{ | |||
return await RunCheckAsync(scope.ServiceProvider, healthCheck, cancellationToken).ConfigureAwait(false); | |||
} | |||
} | |||
/// <summary> | |||
/// Uses the provided service provider and executes the provided check. | |||
/// </summary> | |||
public ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
Guard.ArgumentNotNull(nameof(serviceProvider), serviceProvider); | |||
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck); | |||
return healthCheck.RunAsync(serviceProvider, cancellationToken); | |||
} | |||
/// <summary> | |||
/// Creates a new resolution scope from the default service provider and executes the checks in the given group. | |||
/// </summary> | |||
public async Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
using (var scope = GetServiceScope()) | |||
return await RunGroupAsync(scope.ServiceProvider, group, cancellationToken).ConfigureAwait(false); | |||
} | |||
/// <summary> | |||
/// Uses the provided service provider and executes the checks in the given group. | |||
/// </summary> | |||
public async Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)) | |||
{ | |||
var result = new CompositeHealthCheckResult(group.PartiallyHealthyStatus); | |||
var checkTasks = group.Checks.Select(check => new { Check = check, Task = check.RunAsync(serviceProvider, cancellationToken).AsTask() }).ToList(); | |||
await Task.WhenAll(checkTasks.Select(checkTask => checkTask.Task)); | |||
foreach (var checkTask in checkTasks) | |||
{ | |||
result.Add(checkTask.Check.Name, checkTask.Task.Result); | |||
} | |||
return result; | |||
} | |||
private class UnscopedServiceProvider : IServiceScope | |||
{ | |||
public UnscopedServiceProvider(IServiceProvider serviceProvider) | |||
=> ServiceProvider = serviceProvider; | |||
public IServiceProvider ServiceProvider { get; } | |||
public void Dispose() { } | |||
} | |||
} | |||
} |
@ -1,30 +0,0 @@ | |||
// 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.Linq; | |||
using Microsoft.Extensions.HealthChecks; | |||
namespace Microsoft.Extensions.DependencyInjection | |||
{ | |||
public static class HealthCheckServiceCollectionExtensions | |||
{ | |||
private static readonly Type HealthCheckServiceInterface = typeof(IHealthCheckService); | |||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checks) | |||
{ | |||
Guard.OperationValid(!services.Any(descriptor => descriptor.ServiceType == HealthCheckServiceInterface), "AddHealthChecks may only be called once."); | |||
var builder = new HealthCheckBuilder(); | |||
services.AddSingleton<IHealthCheckService, HealthCheckService>(serviceProvider => | |||
{ | |||
var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); | |||
return new HealthCheckService(builder, serviceProvider, serviceScopeFactory); | |||
}); | |||
checks(builder); | |||
return services; | |||
} | |||
} | |||
} |
@ -1,13 +0,0 @@ | |||
// 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.Threading; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public interface IHealthCheck | |||
{ | |||
ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken)); | |||
} | |||
} |
@ -1,14 +0,0 @@ | |||
// 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.Collections.Generic; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public interface IHealthCheckResult | |||
{ | |||
CheckStatus CheckStatus { get; } | |||
string Description { get; } | |||
IReadOnlyDictionary<string, object> Data { get; } | |||
} | |||
} |
@ -1,58 +0,0 @@ | |||
// 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 System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks | |||
{ | |||
public interface IHealthCheckService | |||
{ | |||
/// <summary> | |||
/// Runs all registered health checks. | |||
/// </summary> | |||
Task<CompositeHealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)); | |||
/// <summary> | |||
/// Gets all registered health checks as a flat list. | |||
/// </summary> | |||
IReadOnlyList<CachedHealthCheck> GetAllChecks(); | |||
/// <summary> | |||
/// Gets a health check by name. | |||
/// </summary> | |||
CachedHealthCheck GetCheck(string checkName); | |||
/// <summary> | |||
/// Gets all health checks in a group. | |||
/// </summary> | |||
HealthCheckGroup GetGroup(string groupName); | |||
/// <summary> | |||
/// Gets all the health check groups. | |||
/// </summary> | |||
IReadOnlyList<HealthCheckGroup> GetGroups(); | |||
/// <summary> | |||
/// Creates a new resolution scope from the default service provider and executes the provided check. | |||
/// </summary> | |||
ValueTask<IHealthCheckResult> RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)); | |||
/// <summary> | |||
/// Uses the provided service provider and executes the provided check. | |||
/// </summary> | |||
ValueTask<IHealthCheckResult> RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)); | |||
/// <summary> | |||
/// Creates a new resolution scope from the default service provider and executes the checks in the given group. | |||
/// </summary> | |||
Task<CompositeHealthCheckResult> RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)); | |||
/// <summary> | |||
/// Uses the provided service provider and executes the checks in the given group. | |||
/// </summary> | |||
Task<CompositeHealthCheckResult> RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)); | |||
} | |||
} |
@ -1,68 +0,0 @@ | |||
// 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 System.Net.Http; | |||
using System.Net.Http.Headers; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.Extensions.HealthChecks.Internal | |||
{ | |||
public class UrlChecker | |||
{ | |||
private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc; | |||
private readonly string _url; | |||
public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, string url) | |||
{ | |||
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); | |||
Guard.ArgumentNotNullOrEmpty(nameof(url), url); | |||
_checkFunc = checkFunc; | |||
_url = url; | |||
} | |||
public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning; | |||
public async Task<IHealthCheckResult> CheckAsync() | |||
{ | |||
using (var httpClient = CreateHttpClient()) | |||
{ | |||
try | |||
{ | |||
var response = await httpClient.GetAsync(_url).ConfigureAwait(false); | |||
return await _checkFunc(response); | |||
} | |||
catch (Exception ex) | |||
{ | |||
var data = new Dictionary<string, object> { { "url", _url } }; | |||
return HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}", data); | |||
} | |||
} | |||
} | |||
private HttpClient CreateHttpClient() | |||
{ | |||
var httpClient = GetHttpClient(); | |||
httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; | |||
return httpClient; | |||
} | |||
public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response) | |||
{ | |||
var status = response.IsSuccessStatusCode ? CheckStatus.Healthy : CheckStatus.Unhealthy; | |||
var data = new Dictionary<string, object> | |||
{ | |||
{ "url", response.RequestMessage.RequestUri.ToString() }, | |||
{ "status", (int)response.StatusCode }, | |||
{ "reason", response.ReasonPhrase }, | |||
{ "body", await response.Content?.ReadAsStringAsync() } | |||
}; | |||
return HealthCheckResult.FromStatus(status, $"status code {response.StatusCode} ({(int)response.StatusCode})", data); | |||
} | |||
protected virtual HttpClient GetHttpClient() | |||
=> new HttpClient(); | |||
} | |||
} |
@ -1,20 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0-preview3-35497" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> | |||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,57 +0,0 @@ | |||
// 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; | |||
static class Guard | |||
{ | |||
public static void ArgumentNotNull(string argumentName, object value) | |||
{ | |||
if (value == null) | |||
{ | |||
throw new ArgumentNullException(argumentName); | |||
} | |||
} | |||
public static void ArgumentNotNullOrEmpty(string argumentName, string value) | |||
{ | |||
if (value == null) | |||
{ | |||
throw new ArgumentNullException(argumentName); | |||
} | |||
if (string.IsNullOrEmpty(value)) | |||
{ | |||
throw new ArgumentException("Value cannot be an empty string.", argumentName); | |||
} | |||
} | |||
// Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration | |||
public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items) | |||
{ | |||
if (items == null) | |||
{ | |||
throw new ArgumentNullException(argumentName); | |||
} | |||
if (items.Count == 0) | |||
{ | |||
throw new ArgumentException("Collection must contain at least one item.", argumentName); | |||
} | |||
} | |||
public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage) | |||
{ | |||
if (!valid) | |||
{ | |||
throw new ArgumentException(exceptionMessage, argumentName); | |||
} | |||
} | |||
public static void OperationValid(bool valid, string exceptionMessage) | |||
{ | |||
if (!valid) | |||
{ | |||
throw new InvalidOperationException(exceptionMessage); | |||
} | |||
} | |||
} |
@ -1,18 +0,0 @@ | |||
using Microsoft.Extensions.HealthChecks; | |||
using System; | |||
namespace WebStatus.Extensions | |||
{ | |||
public static class HealthCheckBuilderExtensions | |||
{ | |||
public static HealthCheckBuilder AddUrlCheckIfNotNull(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration) | |||
{ | |||
if (!string.IsNullOrEmpty(url)) | |||
{ | |||
builder.AddUrlCheck(url, cacheDuration); | |||
} | |||
return builder; | |||
} | |||
} | |||
} |
@ -1,23 +0,0 @@ | |||
using Microsoft.Extensions.HealthChecks; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
namespace WebStatus.Viewmodels | |||
{ | |||
public class HealthStatusViewModel | |||
{ | |||
private readonly CheckStatus _overall; | |||
private readonly Dictionary<string, IHealthCheckResult> _results; | |||
public CheckStatus OverallStatus => _overall; | |||
public IEnumerable<NamedCheckResult> Results => _results.Select(kvp => new NamedCheckResult(kvp.Key, kvp.Value)); | |||
private HealthStatusViewModel() => _results = new Dictionary<string, IHealthCheckResult>(); | |||
public HealthStatusViewModel(CheckStatus overall) : this() => _overall = overall; | |||
public void AddResult(string name, IHealthCheckResult result) => _results.Add(name, result); | |||
} | |||
} |
@ -1,17 +0,0 @@ | |||
using Microsoft.Extensions.HealthChecks; | |||
namespace WebStatus.Viewmodels | |||
{ | |||
public class NamedCheckResult | |||
{ | |||
public string Name { get; } | |||
public IHealthCheckResult Result { get; } | |||
public NamedCheckResult(string name, IHealthCheckResult result) | |||
{ | |||
Name = name; | |||
Result = result; | |||
} | |||
} | |||
} |
@ -1,51 +0,0 @@ | |||
@using Microsoft.AspNetCore.Html | |||
@using Microsoft.Extensions.HealthChecks | |||
@model WebStatus.Viewmodels.HealthStatusViewModel | |||
@{ | |||
ViewData["Title"] = "System Status"; | |||
} | |||
@functions | |||
{ | |||
static readonly string[] LabelClass = new[] { "default", "danger", "success", "warning" }; | |||
public HtmlString StatusLabel(CheckStatus status) | |||
{ | |||
return new HtmlString($@"<span class=""label label-{LabelClass[(int) status]}"">{status}</span>"); | |||
} | |||
} | |||
<style>.label {font-size: 100%}</style> | |||
<div class="row"> | |||
<div class="col-md-12"> | |||
<h2 class="overall-status-title">Overall Status: @StatusLabel(Model.OverallStatus)</h2> | |||
</div> | |||
</div> | |||
<div class="list-group-status"> | |||
@foreach (var result in Model.Results) | |||
{ | |||
<div class="row list-group-status-item"> | |||
<div class="col-md-9"> | |||
<h4 class="list-group-status-item-title">@result.Name</h4> | |||
<p class="list-group-item-text"> | |||
@if (result.Result.Data.ContainsKey("url")) | |||
{ | |||
<p>@result.Result.Data["url"]</p> | |||
} | |||
<p class="text-@(LabelClass[(int)result.Result.CheckStatus])" style="font-weight:bold"> | |||
@result.Result.Description | |||
</p> | |||
</p> | |||
</div> | |||
<div class="col-md-3"> | |||
<h3>@StatusLabel(result.Result.CheckStatus)</h3> | |||
</div> | |||
</div> | |||
} | |||
</div> |
@ -1,14 +0,0 @@ | |||
@{ | |||
ViewData["Title"] = "Error"; | |||
} | |||
<h1 class="text-danger">Error.</h1> | |||
<h2 class="text-danger">An error occurred while processing your request.</h2> | |||
<h3>Development Mode</h3> | |||
<p> | |||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred. | |||
</p> | |||
<p> | |||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application. | |||
</p> |
@ -1,60 +0,0 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>@ViewData["Title"] - WebStatus</title> | |||
<environment names="Development"> | |||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> | |||
<link rel="stylesheet" href="~/css/site.css" /> | |||
</environment> | |||
<environment names="Staging,Production"> | |||
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/4.1.1/css/bootstrap.min.css" | |||
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" | |||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> | |||
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> | |||
</environment> | |||
@if (ViewBag.RefreshSeconds != null && ViewBag.RefreshSeconds > 0) | |||
{ | |||
<meta http-equiv="refresh" content="@ViewBag.RefreshSeconds"> | |||
} | |||
</head> | |||
<body> | |||
<nav class="navbar navbar-dark bg-dark fixed-top"> | |||
<a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand"> | |||
 <b>eShopOnContainers</b> WebStatus | |||
</a> | |||
</nav> | |||
<br /> | |||
<div class="container body-content"> | |||
@RenderBody() | |||
</div> | |||
<br /> | |||
<div id="footer"> | |||
<p> © 2017 - WebStatus</p> | |||
</div> | |||
<environment names="Development"> | |||
<script src="~/lib/jquery/jquery.js"></script> | |||
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> | |||
</environment> | |||
<environment names="Staging,Production"> | |||
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" | |||
asp-fallback-src="~/lib/jquery/jquery.min.js" | |||
asp-fallback-test="window.jQuery" | |||
crossorigin="anonymous" | |||
integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"> | |||
</script> | |||
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/4.1.1/bootstrap.min.js" | |||
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" | |||
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" | |||
crossorigin="anonymous" | |||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"> | |||
</script> | |||
</environment> | |||
</body> | |||
</html> |
@ -1,2 +0,0 @@ | |||
@using WebStatus | |||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
@ -1,3 +0,0 @@ | |||
@{ | |||
Layout = "_Layout"; | |||
} |
@ -1,21 +1,84 @@ | |||
{ | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Debug", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"OrderingUrl": "http://localhost:5102/hc", | |||
"OrderingBackgroundTasksUrl": "http://localhost:5111/hc", | |||
"BasketUrl": "http://localhost:5103/hc", | |||
"CatalogUrl": "http://localhost:5101/hc", | |||
"IdentityUrl": "http://localhost:5105/hc", | |||
"MarketingUrl": "http://localhost:5110/hc", | |||
"LocationsUrl": "http://localhost:5109/hc", | |||
"PaymentUrl": "http://localhost:5108/hc", | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
"HealthChecks-UI": { | |||
"HealthChecks": [ | |||
{ | |||
"Name": "Ordering HTTP Check", | |||
"Uri": "http://localhost:5102/hc" | |||
}, | |||
{ | |||
"Name": "Ordering HTTP Background Check", | |||
"Uri": "http://localhost:5111/hc" | |||
}, | |||
{ | |||
"Name": "Basket HTTP Check", | |||
"Uri": "http://localhost:5103/hc" | |||
}, | |||
{ | |||
"Name": "Catalog HTTP Check", | |||
"Uri": "http://localhost:5101/hc" | |||
}, | |||
{ | |||
"Name": "Identity HTTP Check", | |||
"Uri": "http://localhost:5105/hc" | |||
}, | |||
{ | |||
"Name": "Marketing HTTP Check", | |||
"Uri": "http://localhost:5110/hc" | |||
}, | |||
{ | |||
"Name": "Locations HTTP Check", | |||
"Uri": "http://localhost:5109/hc" | |||
}, | |||
{ | |||
"Name": "Payments HTTP Check", | |||
"Uri": "http://localhost:5108/hc" | |||
}, | |||
{ | |||
"Name": "WebMVC HTTP Check", | |||
"Uri": "http://localhost:5100/hc" | |||
}, | |||
{ | |||
"Name": "WebSPA HTTP Check", | |||
"Uri": "http://localhost:5104/hc" | |||
}, | |||
{ | |||
"Name": "SignalR HTTP Check", | |||
"Uri": "http://localhost:5112/hc" | |||
}, | |||
{ | |||
"Name": "Mobile Shopping API GW HTTP Check", | |||
"Uri": "http://localhost:5200/hc" | |||
}, | |||
{ | |||
"Name": "Mobile Marketing API GW HTTP Check", | |||
"Uri": "http://localhost:5201/hc" | |||
}, | |||
{ | |||
"Name": "Web Shopping API GW HTTP Check", | |||
"Uri": "http://localhost:5202/hc" | |||
}, | |||
{ | |||
"Name": "Web Marketing API GW HTTP Check", | |||
"Uri": "http://localhost:5203/hc" | |||
}, | |||
{ | |||
"Name": "Mobile Shopping Aggregator HTTP Check", | |||
"Uri": "http://localhost:5120/hc" | |||
}, | |||
{ | |||
"Name": "Web Shopping Aggregator HTTP Check", | |||
"Uri": "http://localhost:5121/hc" | |||
} | |||
], | |||
"Webhooks": [ | |||
{ | |||
"Name": "", | |||
"Uri": "", | |||
"Payload": "", | |||
"RestoredPayload": "" | |||
} | |||
], | |||
"EvaluationTimeOnSeconds": 10, | |||
"MinimumSecondsBetweenFailureNotifications": 60 | |||
} | |||
} |