diff --git a/HealthAzure.sln b/HealthAzure.sln index 7b3aa46..03eace7 100644 --- a/HealthAzure.sln +++ b/HealthAzure.sln @@ -49,6 +49,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthAzureSandbox", "sandb EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Health.Checks.AzureDocumentDB", "src\App.Metrics.Health.Checks.AzureDocumentDB\App.Metrics.Health.Checks.AzureDocumentDB.csproj", "{FDA9F929-C56D-424F-A4B5-8B551931C39C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Health.Checks.AzureServiceBus", "src\App.Metrics.Health.Checks.AzureServiceBus\App.Metrics.Health.Checks.AzureServiceBus.csproj", "{452F4D94-36E8-4AB0-BD5D-01E5C160E465}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.Health.Checks.AzureEventHubs", "src\App.Metrics.Health.Checks.AzureEventHubs\App.Metrics.Health.Checks.AzureEventHubs.csproj", "{1AC63139-9B03-4936-9ABF-300523E4522F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -67,6 +71,14 @@ Global {FDA9F929-C56D-424F-A4B5-8B551931C39C}.Debug|Any CPU.Build.0 = Debug|Any CPU {FDA9F929-C56D-424F-A4B5-8B551931C39C}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDA9F929-C56D-424F-A4B5-8B551931C39C}.Release|Any CPU.Build.0 = Release|Any CPU + {452F4D94-36E8-4AB0-BD5D-01E5C160E465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {452F4D94-36E8-4AB0-BD5D-01E5C160E465}.Debug|Any CPU.Build.0 = Debug|Any CPU + {452F4D94-36E8-4AB0-BD5D-01E5C160E465}.Release|Any CPU.ActiveCfg = Release|Any CPU + {452F4D94-36E8-4AB0-BD5D-01E5C160E465}.Release|Any CPU.Build.0 = Release|Any CPU + {1AC63139-9B03-4936-9ABF-300523E4522F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AC63139-9B03-4936-9ABF-300523E4522F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AC63139-9B03-4936-9ABF-300523E4522F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AC63139-9B03-4936-9ABF-300523E4522F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -78,6 +90,8 @@ Global {38E7AD0C-9792-4D2D-8C2C-9D9CE6BA210B} = {2D805782-756E-4C98-B22E-F502BEE95318} {6CEA2D2A-FCDD-4C5C-98F8-6C69DFB3F8F9} = {C0BC96C5-46E1-4323-9C5D-58D9A96549CD} {FDA9F929-C56D-424F-A4B5-8B551931C39C} = {2D805782-756E-4C98-B22E-F502BEE95318} + {452F4D94-36E8-4AB0-BD5D-01E5C160E465} = {2D805782-756E-4C98-B22E-F502BEE95318} + {1AC63139-9B03-4936-9ABF-300523E4522F} = {2D805782-756E-4C98-B22E-F502BEE95318} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A8699A1D-0BA1-403C-86E9-C789CD2645EB} diff --git a/README.md b/README.md index 77a2626..75a7b16 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ App Metrics is an open-source and cross-platform .NET library used to record met |------|:--------:|:--------:|:--------:| |App.Metrics.Health.Checks.AzureDocumentDB|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Health.Checks.AzureDocumentDB.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Health.Checks.AzureDocumentDB)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Health.Checks.AzureDocumentDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureDocumentDB/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Health.Checks.AzureDocumentDB.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureDocumentDB/) |App.Metrics.Health.Checks.AzureStorage|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Health.Checks.AzureStorage.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Health.Checks.AzureStorage)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Health.Checks.AzureStorage.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureStorage/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Health.Checks.AzureStorage.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureStorage/)| +|App.Metrics.Health.Checks.AzureServiceBus|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Health.Checks.AzureServiceBus.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Health.Checks.AzureServiceBus)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Health.Checks.AzureServiceBus.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureServiceBus/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Health.Checks.AzureServiceBus.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureServiceBus/)| +|App.Metrics.Health.Checks.AzureEventHubs|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.Health.Checks.AzureEventHubs.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.Health.Checks.AzureEventHubs)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.Health.Checks.AzureEventHubs.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureEventHubs/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.Health.Checks.AzureEventHubs.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.Health.Checks.AzureEventHubs/)| ## How to build diff --git a/appveyor.yml b/appveyor.yml index 54e6031..906b5ef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,9 +30,6 @@ deploy: secure: gIAiACgNj+JzXyLLTe3rLxZyrAB9RpC8Lw81xEjdOLXqotprqEwGiFWRipEqkpps skip_symbols: true symbol_server: https://www.myget.org/F/appmetrics/symbol -install: - - cmd: curl -O https://download.microsoft.com/download/8/8/5/88544F33-836A-49A5-8B67-451C24709A8F/dotnet-sdk-2.1.300-win-x64.exe - - cmd: dotnet-sdk-2.1.300-win-x64.exe /install /quiet /norestart /log install.log skip_commits: files: - '**/*.md' \ No newline at end of file diff --git a/build.cake b/build.cake index 7e3d1cf..1e734bd 100644 --- a/build.cake +++ b/build.cake @@ -39,7 +39,9 @@ var linkSources = HasArgument("LinkSources") ? Argument("LinkSources") ////////////////////////////////////////////////////////////////////// var packDirs = new [] { Directory("./src/App.Metrics.Health.Checks.AzureStorage"), - Directory("./src/App.Metrics.Health.Checks.AzureDocumentDB") + Directory("./src/App.Metrics.Health.Checks.AzureDocumentDB"), + Directory("./src/App.Metrics.Health.Checks.AzureServiceBus"), + Directory("./src/App.Metrics.Health.Checks.AzureEventHubs") }; var artifactsDir = (DirectoryPath) Directory("./artifacts"); var testResultsDir = (DirectoryPath) artifactsDir.Combine("test-results"); diff --git a/build/dependencies.props b/build/dependencies.props index 499269e..47b0a24 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -1,16 +1,17 @@ - 2.1.0 + 2.1.0-* + 2.1.1 0.10.14 4.5.0 2.1.0-* 5.0.0 4.5.0 - 4.8.2 - 11.0.2 - 15.8.0-preview-20180510-03 - 2.4.0-beta.2.build4010 - 5.3.2 + 4.10.0 + 12.0.1 + 15.9.0 + 2.4.1 + 5.5.3 5.0.0 1.0.0 diff --git a/global.json b/global.json index 2c32e12..4560569 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "projects": [ "src", "test", "benchmarks", "sandbox" ], "sdk": { - "version": "2.1.300" + "version": "2.1.301" } } \ No newline at end of file diff --git a/sandbox/HealthAzureSandbox/HealthAzureSandbox.csproj b/sandbox/HealthAzureSandbox/HealthAzureSandbox.csproj index 54623e5..dfb4f5e 100644 --- a/sandbox/HealthAzureSandbox/HealthAzureSandbox.csproj +++ b/sandbox/HealthAzureSandbox/HealthAzureSandbox.csproj @@ -15,6 +15,8 @@ + + diff --git a/sandbox/HealthAzureSandbox/Host.cs b/sandbox/HealthAzureSandbox/Host.cs index 8bce43d..710dd32 100644 --- a/sandbox/HealthAzureSandbox/Host.cs +++ b/sandbox/HealthAzureSandbox/Host.cs @@ -8,8 +8,6 @@ using System.Threading; using System.Threading.Tasks; using App.Metrics.Health; -using App.Metrics.Health.Checks.AzureDocumentDB; -using App.Metrics.Health.Checks.AzureStorage; using Microsoft.Azure.Documents.Client; using Microsoft.Extensions.Configuration; using Microsoft.WindowsAzure.Storage; @@ -72,20 +70,28 @@ private static void Init() var containerName = "test"; var documentDbDatabaseUri = UriFactory.CreateDatabaseUri("test"); var collectionUri = UriFactory.CreateDocumentCollectionUri("test", "testcollection"); - var documentDbClient = new DocumentClient( - new Uri("https://localhost:8081"), - "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="); + var doucmentDbKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + var documentDbUri = "https://localhost:8081"; var storageAccount = CloudStorageAccount.Parse(@"UseDevelopmentStorage=true"); + var eventHubConnectionString = "todo event hub connection string"; + var eventHubName = "todo event hub name"; + var serviceBusConnectionString = "todo sb connection string"; + var queueName = "todo queue name"; + var topicName = "todo topic name"; + Health = AppMetricsHealth.CreateDefaultBuilder() - .HealthChecks.AddAzureBlobStorageConnectivityCheck("Blob Storage Conectivity Check", storageAccount) + .HealthChecks.AddAzureBlobStorageConnectivityCheck("Blob Storage Connectivity Check", storageAccount) .HealthChecks.AddAzureBlobStorageContainerCheck("Blob Storage Container Check", storageAccount, containerName) .HealthChecks.AddAzureQueueStorageConnectivityCheck("Queue Storage Connectivity Check", storageAccount) .HealthChecks.AddAzureQueueStorageCheck("Queue Storage Check", storageAccount, "test") - .HealthChecks.AddAzureDocumentDBDatabaseCheck("DocumentDB Database Check", documentDbDatabaseUri, documentDbClient) - .HealthChecks.AddAzureDocumentDBCollectionCheck("DocumentDB Collection Check", collectionUri, documentDbClient) + .HealthChecks.AddAzureDocumentDBDatabaseCheck("DocumentDB Database Check", documentDbDatabaseUri, documentDbUri, doucmentDbKey) + .HealthChecks.AddAzureDocumentDBCollectionCheck("DocumentDB Collection Check", collectionUri, documentDbUri, doucmentDbKey) .HealthChecks.AddAzureTableStorageConnectivityCheck("Table Storage Connectivity Check", storageAccount) .HealthChecks.AddAzureTableStorageTableCheck("Table Storage Table Exists Check", storageAccount, "test") + .HealthChecks.AddAzureEventHubConnectivityCheck("Service EventHub Connectivity Check", eventHubConnectionString, eventHubName) + .HealthChecks.AddAzureServiceBusQueueConnectivityCheck("Service Bus Queue Connectivity Check", serviceBusConnectionString, queueName) + .HealthChecks.AddAzureServiceBusTopicConnectivityCheck("Service Bus Topic Connectivity Check", serviceBusConnectionString, topicName) .Build(); } diff --git a/src/App.Metrics.Health.Checks.AzureDocumentDB/App.Metrics.Health.Checks.AzureDocumentDB.csproj b/src/App.Metrics.Health.Checks.AzureDocumentDB/App.Metrics.Health.Checks.AzureDocumentDB.csproj index 9b22dc2..99e5d3e 100644 --- a/src/App.Metrics.Health.Checks.AzureDocumentDB/App.Metrics.Health.Checks.AzureDocumentDB.csproj +++ b/src/App.Metrics.Health.Checks.AzureDocumentDB/App.Metrics.Health.Checks.AzureDocumentDB.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/src/App.Metrics.Health.Checks.AzureDocumentDB/AzureDocumentDBHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureDocumentDB/AzureDocumentDBHealthCheckBuilderExtensions.cs index e61c015..039b922 100644 --- a/src/App.Metrics.Health.Checks.AzureDocumentDB/AzureDocumentDBHealthCheckBuilderExtensions.cs +++ b/src/App.Metrics.Health.Checks.AzureDocumentDB/AzureDocumentDBHealthCheckBuilderExtensions.cs @@ -4,12 +4,15 @@ using System; using System.Net; +using System.Threading; using System.Threading.Tasks; using App.Metrics.Health.Logging; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Client; -namespace App.Metrics.Health.Checks.AzureDocumentDB +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace { public static class AzureDocumentDBHealthCheckBuilderExtensions { @@ -30,6 +33,22 @@ public static IHealthBuilder AddAzureDocumentDBCollectionCheck( return builder.Builder; } + public static IHealthBuilder AddAzureDocumentDBCollectionCheck( + this IHealthCheckBuilder builder, + string name, + Uri collectionUri, + string endpointUri, + string key, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckDocumentDbCollectionExists(collectionUri, endpointUri, key), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureDocumentDBCollectionCheck( this IHealthCheckBuilder builder, string name, @@ -41,6 +60,18 @@ public static IHealthBuilder AddAzureDocumentDBCollectionCheck( return builder.Builder; } + public static IHealthBuilder AddAzureDocumentDBCollectionCheck( + this IHealthCheckBuilder builder, + string name, + Uri collectionUri, + string endpointUri, + string key) + { + builder.AddCheck(name, CheckDocumentDbCollectionExists(collectionUri, endpointUri, key)); + + return builder.Builder; + } + public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( this IHealthCheckBuilder builder, string name, @@ -56,6 +87,22 @@ public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( return builder.Builder; } + public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( + this IHealthCheckBuilder builder, + string name, + Uri databaseUri, + string endpointUri, + string key, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckDocumentDBDatabaseConnectivity(databaseUri, endpointUri, key), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( this IHealthCheckBuilder builder, string name, @@ -67,6 +114,52 @@ public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( return builder.Builder; } + public static IHealthBuilder AddAzureDocumentDBDatabaseCheck( + this IHealthCheckBuilder builder, + string name, + Uri databaseUri, + string endpointUri, + string key) + { + builder.AddCheck(name, CheckDocumentDBDatabaseConnectivity(databaseUri, endpointUri, key)); + + return builder.Builder; + } + + private static Func> CheckDocumentDbCollectionExists(Uri collectionUri, string endpointUri, string key) + { + return async () => + { + bool result; + + try + { + using (var documentClient = new DocumentClient(new Uri(endpointUri), key)) + { + var database = await documentClient.ReadDocumentCollectionAsync(collectionUri); + + result = database?.StatusCode == HttpStatusCode.OK; + } + } + catch (DocumentClientException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + Logger.ErrorException($"{collectionUri} was not found.", ex); + + result = false; + } + catch (Exception ex) + { + Logger.ErrorException($"{collectionUri} failed.", ex); + + result = false; + } + + return result + ? HealthCheckResult.Healthy($"OK. '{collectionUri}' is available.") + : HealthCheckResult.Unhealthy($"Failed. '{collectionUri}' is unavailable."); + }; + } + private static Func> CheckDocumentDbCollectionExists(Uri collectionUri, DocumentClient documentClient) { return async () => @@ -128,5 +221,44 @@ private static Func> CheckDocumentDBDatabaseConnect : HealthCheckResult.Unhealthy($"Failed. '{databaseUri}' is unavailable."); }; } + + private static Func> CheckDocumentDBDatabaseConnectivity(Uri databaseUri, string endpointUri, string key) + { + return async () => + { + bool result; + + try + { + using (var documentClient = new DocumentClient(new Uri(endpointUri), key)) + { + var token = new CancellationTokenSource(); + token.CancelAfter(TimeSpan.FromSeconds(10)); + + await documentClient.OpenAsync(token.Token).ConfigureAwait(false); + + var database = await documentClient.ReadDatabaseAsync(databaseUri); + + result = database?.StatusCode == HttpStatusCode.OK; + } + } + catch (DocumentClientException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + Logger.ErrorException($"{databaseUri} was not found.", ex); + + result = false; + } + catch (Exception ex) + { + Logger.ErrorException($"{databaseUri} failed.", ex); + + result = false; + } + + return result + ? HealthCheckResult.Healthy($"OK. '{databaseUri}' is available.") + : HealthCheckResult.Unhealthy($"Failed. '{databaseUri}' is unavailable."); + }; + } } } \ No newline at end of file diff --git a/src/App.Metrics.Health.Checks.AzureEventHubs/App.Metrics.Health.Checks.AzureEventHubs.csproj b/src/App.Metrics.Health.Checks.AzureEventHubs/App.Metrics.Health.Checks.AzureEventHubs.csproj new file mode 100644 index 0000000..0f1db86 --- /dev/null +++ b/src/App.Metrics.Health.Checks.AzureEventHubs/App.Metrics.Health.Checks.AzureEventHubs.csproj @@ -0,0 +1,15 @@ + + + + App Metrics Azure EventHubs health checks. + netstandard2.0 + true + appmetrics;healthchecks;azure;eventhubs + + + + + + + + diff --git a/src/App.Metrics.Health.Checks.AzureEventHubs/AzureEventHubHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureEventHubs/AzureEventHubHealthCheckBuilderExtensions.cs new file mode 100644 index 0000000..9ecd199 --- /dev/null +++ b/src/App.Metrics.Health.Checks.AzureEventHubs/AzureEventHubHealthCheckBuilderExtensions.cs @@ -0,0 +1,73 @@ +// +// Copyright (c) App Metrics Contributors. All rights reserved. +// + +using System; +using System.Threading.Tasks; +using App.Metrics.Health.Logging; +using Microsoft.Azure.EventHubs; + +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace +{ + public static class AzureEventHubHealthCheckBuilderExtensions + { + private static readonly ILog Logger = LogProvider.For(); + + public static IHealthBuilder AddAzureEventHubConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + EventHubClient eventHubClient, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckEventHubConnectivity(name, eventHubClient), + cacheDuration); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureEventHubConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string eventHubName) + { + var connectionStringBuilder = new EventHubsConnectionStringBuilder(connectionString) + { + EntityPath = eventHubName + }; + + var eventHubClient = EventHubClient.CreateFromConnectionString(connectionStringBuilder.ToString()); + + builder.AddCheck(name, CheckEventHubConnectivity(name, eventHubClient)); + + return builder.Builder; + } + + private static Func> CheckEventHubConnectivity(string name, EventHubClient eventHubClient) + { + return async () => + { + var result = true; + + try + { + await eventHubClient.GetRuntimeInformationAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException($"{name} failed.", ex); + + result = false; + } + + return result + ? HealthCheckResult.Healthy($"OK. '{eventHubClient.EventHubName}' is available.") + : HealthCheckResult.Unhealthy($"Failed. '{eventHubClient.EventHubName}' is unavailable."); + }; + } + } +} \ No newline at end of file diff --git a/src/App.Metrics.Health.Checks.AzureServiceBus/App.Metrics.Health.Checks.AzureServiceBus.csproj b/src/App.Metrics.Health.Checks.AzureServiceBus/App.Metrics.Health.Checks.AzureServiceBus.csproj new file mode 100644 index 0000000..10676d6 --- /dev/null +++ b/src/App.Metrics.Health.Checks.AzureServiceBus/App.Metrics.Health.Checks.AzureServiceBus.csproj @@ -0,0 +1,15 @@ + + + + App Metrics Azure Service Bus health checks. + netstandard2.0 + true + appmetrics;healthchecks;azure;servicebus;topic;queue + + + + + + + + diff --git a/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusQueueHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusQueueHealthCheckBuilderExtensions.cs new file mode 100644 index 0000000..eed41d0 --- /dev/null +++ b/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusQueueHealthCheckBuilderExtensions.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) App Metrics Contributors. All rights reserved. +// + +using System; +using System.Text; +using System.Threading.Tasks; +using App.Metrics.Health.Logging; +using Microsoft.Azure.ServiceBus; + +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace +{ + public static class AzureServiceBusQueueHealthCheckBuilderExtensions + { + private static readonly ILog Logger = LogProvider.For(); + private static readonly Message HealthMessage = new Message(Encoding.UTF8.GetBytes("Queue Health Check")); + private static readonly DateTimeOffset HealthMessageTestSchedule = new DateTimeOffset(DateTime.UtcNow).AddDays(1); + private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); + + public static IHealthBuilder AddAzureServiceBusQueueConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + QueueClient queueClient, + TimeSpan cacheDuration) + { + builder.AddCachedCheck(name, CheckServiceBusQueueConnectivity(name, queueClient), cacheDuration); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusQueueConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + QueueClient queueClient) + { + builder.AddCheck(name, CheckServiceBusQueueConnectivity(name, queueClient)); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusQueueConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string queueName, + TimeSpan cacheDuration) + { + var queueClient = new QueueClient(connectionString, queueName, receiveMode: ReceiveMode.PeekLock) { OperationTimeout = DefaultTimeout }; + + builder.AddCachedCheck(name, CheckServiceBusQueueConnectivity(name, queueClient), cacheDuration); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusQueueConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string queueName) + { + var queueClient = new QueueClient(connectionString, queueName, receiveMode: ReceiveMode.PeekLock) { OperationTimeout = DefaultTimeout }; + + builder.AddCheck(name, CheckServiceBusQueueConnectivity(name, queueClient)); + + return builder.Builder; + } + + private static Func> CheckServiceBusQueueConnectivity(string name, QueueClient queueClient) + { + return async () => + { + var result = true; + + try + { + var id = await queueClient.ScheduleMessageAsync(HealthMessage, HealthMessageTestSchedule).ConfigureAwait(false); + await queueClient.CancelScheduledMessageAsync(id); + } + catch (Exception ex) + { + Logger.ErrorException($"{name} failed.", ex); + + result = false; + } + + return result + ? HealthCheckResult.Healthy($"OK. '{queueClient.Path}/{queueClient.QueueName}' is available.") + : HealthCheckResult.Unhealthy($"Failed. '{queueClient.Path}/{queueClient.QueueName}' is unavailable."); + }; + } + } +} \ No newline at end of file diff --git a/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusTopicHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusTopicHealthCheckBuilderExtensions.cs new file mode 100644 index 0000000..a96ab2c --- /dev/null +++ b/src/App.Metrics.Health.Checks.AzureServiceBus/AzureServiceBusTopicHealthCheckBuilderExtensions.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) App Metrics Contributors. All rights reserved. +// + +using System; +using System.Text; +using System.Threading.Tasks; +using App.Metrics.Health.Logging; +using Microsoft.Azure.ServiceBus; + +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace +{ + public static class AzureServiceBusTopicHealthCheckBuilderExtensions + { + private static readonly ILog Logger = LogProvider.For(); + private static readonly Message HealthMessage = new Message(Encoding.UTF8.GetBytes("Topic Health Check")); + private static readonly DateTimeOffset HealthMessageTestSchedule = new DateTimeOffset(DateTime.UtcNow).AddDays(1); + private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); + + public static IHealthBuilder AddAzureServiceBusTopicConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + TopicClient topicClient, + TimeSpan cacheDuration) + { + builder.AddCachedCheck(name, CheckServiceBusTopicConnectivity(name, topicClient), cacheDuration); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusTopicConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + TopicClient topicClient) + { + builder.AddCheck(name, CheckServiceBusTopicConnectivity(name, topicClient)); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusTopicConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string topicName, + TimeSpan cacheDuration) + { + var topicClient = new TopicClient(connectionString, topicName) { OperationTimeout = DefaultTimeout }; + + builder.AddCachedCheck(name, CheckServiceBusTopicConnectivity(name, topicClient), cacheDuration); + + return builder.Builder; + } + + public static IHealthBuilder AddAzureServiceBusTopicConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string topicName) + { + var topicClient = new TopicClient(connectionString, topicName) { OperationTimeout = DefaultTimeout }; + + builder.AddCheck(name, CheckServiceBusTopicConnectivity(name, topicClient)); + + return builder.Builder; + } + + private static Func> CheckServiceBusTopicConnectivity(string name, TopicClient topicClient) + { + return async () => + { + var result = true; + + try + { + var id = await topicClient.ScheduleMessageAsync(HealthMessage, HealthMessageTestSchedule).ConfigureAwait(false); + await topicClient.CancelScheduledMessageAsync(id); + } + catch (Exception ex) + { + Logger.ErrorException($"{name} failed.", ex); + + result = false; + } + + return result + ? HealthCheckResult.Healthy($"OK. '{topicClient.Path}/{topicClient.TopicName}' is available.") + : HealthCheckResult.Unhealthy($"Failed. '{topicClient.Path}/{topicClient.TopicName}' is unavailable."); + }; + } + } +} \ No newline at end of file diff --git a/src/App.Metrics.Health.Checks.AzureStorage/App.Metrics.Health.Checks.AzureStorage.csproj b/src/App.Metrics.Health.Checks.AzureStorage/App.Metrics.Health.Checks.AzureStorage.csproj index 9678765..6f3c60f 100644 --- a/src/App.Metrics.Health.Checks.AzureStorage/App.Metrics.Health.Checks.AzureStorage.csproj +++ b/src/App.Metrics.Health.Checks.AzureStorage/App.Metrics.Health.Checks.AzureStorage.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/src/App.Metrics.Health.Checks.AzureStorage/AzureBlobStorageHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureStorage/AzureBlobStorageHealthCheckBuilderExtensions.cs index f81bf97..61db3cb 100644 --- a/src/App.Metrics.Health.Checks.AzureStorage/AzureBlobStorageHealthCheckBuilderExtensions.cs +++ b/src/App.Metrics.Health.Checks.AzureStorage/AzureBlobStorageHealthCheckBuilderExtensions.cs @@ -7,7 +7,9 @@ using App.Metrics.Health.Logging; using Microsoft.WindowsAzure.Storage; -namespace App.Metrics.Health.Checks.AzureStorage +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace { public static class AzureBlobStorageHealthCheckBuilderExtensions { @@ -27,6 +29,20 @@ public static IHealthBuilder AddAzureBlobStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureBlobStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckBlobStorageConnectivity(name, CloudStorageAccount.Parse(connectionString)), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureBlobStorageConnectivityCheck( this IHealthCheckBuilder builder, string name, @@ -37,6 +53,16 @@ public static IHealthBuilder AddAzureBlobStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureBlobStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString) + { + builder.AddCheck(name, CheckBlobStorageConnectivity(name, CloudStorageAccount.Parse(connectionString))); + + return builder.Builder; + } + public static IHealthBuilder AddAzureBlobStorageContainerCheck( this IHealthCheckBuilder builder, string name, @@ -52,6 +78,21 @@ public static IHealthBuilder AddAzureBlobStorageContainerCheck( return builder.Builder; } + public static IHealthBuilder AddAzureBlobStorageContainerCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string containerName, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckAzureBlobStorageContainerExists(name, CloudStorageAccount.Parse(connectionString), containerName), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureBlobStorageContainerCheck( this IHealthCheckBuilder builder, string name, @@ -63,6 +104,17 @@ public static IHealthBuilder AddAzureBlobStorageContainerCheck( return builder.Builder; } + public static IHealthBuilder AddAzureBlobStorageContainerCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string containerName) + { + builder.AddCheck(name, CheckAzureBlobStorageContainerExists(name, CloudStorageAccount.Parse(connectionString), containerName)); + + return builder.Builder; + } + private static Func> CheckAzureBlobStorageContainerExists( string name, CloudStorageAccount storageAccount, @@ -78,7 +130,7 @@ private static Func> CheckAzureBlobStorageContainer var blobContainer = queueClient.GetContainerReference(containerName); - result = await blobContainer.ExistsAsync(); + result = await blobContainer.ExistsAsync().ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/App.Metrics.Health.Checks.AzureStorage/AzureQueueStorageHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureStorage/AzureQueueStorageHealthCheckBuilderExtensions.cs index 602f754..a03faca 100644 --- a/src/App.Metrics.Health.Checks.AzureStorage/AzureQueueStorageHealthCheckBuilderExtensions.cs +++ b/src/App.Metrics.Health.Checks.AzureStorage/AzureQueueStorageHealthCheckBuilderExtensions.cs @@ -7,7 +7,9 @@ using App.Metrics.Health.Logging; using Microsoft.WindowsAzure.Storage; -namespace App.Metrics.Health.Checks.AzureStorage +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace { public static class AzureQueueStorageHealthCheckBuilderExtensions { @@ -25,6 +27,18 @@ public static IHealthBuilder AddAzureQueueStorageCheck( return builder.Builder; } + public static IHealthBuilder AddAzureQueueStorageCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string queueName, + TimeSpan cacheDuration) + { + builder.AddCachedCheck(name, CheckQueueExistsAsync(name, CloudStorageAccount.Parse(connectionString), queueName), cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureQueueStorageCheck( this IHealthCheckBuilder builder, string name, @@ -36,6 +50,17 @@ public static IHealthBuilder AddAzureQueueStorageCheck( return builder.Builder; } + public static IHealthBuilder AddAzureQueueStorageCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string queueName) + { + builder.AddCheck(name, CheckQueueExistsAsync(name, CloudStorageAccount.Parse(connectionString), queueName)); + + return builder.Builder; + } + public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( this IHealthCheckBuilder builder, string name, @@ -50,6 +75,20 @@ public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckStorageAccountConnectivity(name, CloudStorageAccount.Parse(connectionString)), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( this IHealthCheckBuilder builder, string name, @@ -62,6 +101,18 @@ public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureQueueStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString) + { + builder.AddCheck( + name, + CheckStorageAccountConnectivity(name, CloudStorageAccount.Parse(connectionString))); + + return builder.Builder; + } + private static Func> CheckQueueExistsAsync(string name, CloudStorageAccount storageAccount, string queueName) { return async () => diff --git a/src/App.Metrics.Health.Checks.AzureStorage/AzureTableStorageHealthCheckBuilderExtensions.cs b/src/App.Metrics.Health.Checks.AzureStorage/AzureTableStorageHealthCheckBuilderExtensions.cs index 12c6cee..56847ec 100644 --- a/src/App.Metrics.Health.Checks.AzureStorage/AzureTableStorageHealthCheckBuilderExtensions.cs +++ b/src/App.Metrics.Health.Checks.AzureStorage/AzureTableStorageHealthCheckBuilderExtensions.cs @@ -7,7 +7,9 @@ using App.Metrics.Health.Logging; using Microsoft.WindowsAzure.Storage; -namespace App.Metrics.Health.Checks.AzureStorage +// ReSharper disable CheckNamespace +namespace App.Metrics.Health + // ReSharper restore CheckNamespace { public static class AzureTableStorageHealthCheckBuilderExtensions { @@ -27,6 +29,20 @@ public static IHealthBuilder AddAzureTableStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureTableStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckTableStorageConnectivity(name, CloudStorageAccount.Parse(connectionString)), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureTableStorageConnectivityCheck( this IHealthCheckBuilder builder, string name, @@ -37,6 +53,16 @@ public static IHealthBuilder AddAzureTableStorageConnectivityCheck( return builder.Builder; } + public static IHealthBuilder AddAzureTableStorageConnectivityCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString) + { + builder.AddCheck(name, CheckTableStorageConnectivity(name, CloudStorageAccount.Parse(connectionString))); + + return builder.Builder; + } + public static IHealthBuilder AddAzureTableStorageTableCheck( this IHealthCheckBuilder builder, string name, @@ -52,6 +78,21 @@ public static IHealthBuilder AddAzureTableStorageTableCheck( return builder.Builder; } + public static IHealthBuilder AddAzureTableStorageTableCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string tableName, + TimeSpan cacheDuration) + { + builder.AddCachedCheck( + name, + CheckAzureTableStorageTableExists(name, CloudStorageAccount.Parse(connectionString), tableName), + cacheDuration); + + return builder.Builder; + } + public static IHealthBuilder AddAzureTableStorageTableCheck( this IHealthCheckBuilder builder, string name, @@ -63,6 +104,17 @@ public static IHealthBuilder AddAzureTableStorageTableCheck( return builder.Builder; } + public static IHealthBuilder AddAzureTableStorageTableCheck( + this IHealthCheckBuilder builder, + string name, + string connectionString, + string tableName) + { + builder.AddCheck(name, CheckAzureTableStorageTableExists(name, CloudStorageAccount.Parse(connectionString), tableName)); + + return builder.Builder; + } + private static Func> CheckAzureTableStorageTableExists( string name, CloudStorageAccount storageAccount, diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 8c188ac..036aa4f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,7 +3,7 @@ - + \ No newline at end of file