Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revive PR - Extend Service Bus with Active and Deadletter-Queue thresholds #1861

Merged
merged 30 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a6d5f4a
Added AzureServiceBusQueueMessageThresholdCountHealthCheck
mclausen Aug 18, 2021
c8439a2
Aligned Naming Health Check
mclausen Aug 18, 2021
320c22f
Added a description for the failing health checks
mclausen Aug 18, 2021
54f5fd6
Added Deadletter Queue Count Message Threshold HealthCheck
mclausen Aug 18, 2021
0deac72
Update src/HealthChecks.AzureServiceBus/AzureServiceBusDeadLetterQueu…
mclausen Oct 27, 2021
7ae4306
Update src/HealthChecks.AzureServiceBus/AzureServiceBusQueueMessageCo…
mclausen Oct 27, 2021
91e2f92
Update test/UnitTests/DependencyInjection/AzureServiceBus/AzureServic…
mclausen Oct 27, 2021
1ba79cb
Update test/UnitTests/DependencyInjection/AzureServiceBus/AzureServic…
mclausen Oct 27, 2021
4544266
Update test/UnitTests/DependencyInjection/AzureServiceBus/AzureServic…
mclausen Oct 27, 2021
480c864
Update test/UnitTests/DependencyInjection/AzureServiceBus/AzureServic…
mclausen Oct 27, 2021
bff7a23
Merge branch 'master' into feature/service-bus
cieciurm Jul 5, 2023
09c91cc
Update
cieciurm Jul 5, 2023
7c9cc76
File-scoped namespace and inhertic docs
cieciurm Jul 5, 2023
676ed29
API approved
cieciurm Jul 5, 2023
576eb80
Update HC
cieciurm Jul 6, 2023
e12676a
Fix format
cieciurm Jul 6, 2023
056c825
Update src/HealthChecks.AzureServiceBus/AzureServiceBusQueueMessageCo…
sungam3r Jul 6, 2023
b2b7bd0
Update
cieciurm Jul 6, 2023
0dfdc36
Apply suggestions from code review
sungam3r Jul 6, 2023
3ef0b39
Apply suggestions from code review
sungam3r Jul 6, 2023
03b0dc2
Fix build
cieciurm Jul 6, 2023
aff28aa
Single name
cieciurm Jul 6, 2023
a4d8d5e
Update
cieciurm Jul 6, 2023
458988b
Update
cieciurm Jul 6, 2023
a617244
tests
cieciurm Jul 6, 2023
e9f5999
fix tests
cieciurm Jul 6, 2023
19fe25e
mock mock token credentials
cieciurm Jul 6, 2023
fef3d30
fix format
cieciurm Jul 6, 2023
158bb50
using var serviceProvider
cieciurm Jul 6, 2023
f98df61
Apply suggestions from code review
sungam3r Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Azure.Messaging.ServiceBus.Administration;
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
using HealthChecks.AzureServiceBus.Configuration;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace HealthChecks.AzureServiceBus;

public class AzureServiceBusQueueMessageCountThresholdHealthCheck : AzureServiceBusHealthCheck<AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions>, IHealthCheck
cieciurm marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly bool _checkDeadLetterMessages;
private readonly string _queueName;
private readonly int _degradedThreshold;
private readonly int _unhealthyThreshold;

public AzureServiceBusQueueMessageCountThresholdHealthCheck(AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions options)
: base(options)
{
_checkDeadLetterMessages = options.CheckDeadLetterMessages;
_queueName = Guard.ThrowIfNull(options.QueueName);

var threshold = GetThreshold(options);

_degradedThreshold = threshold.DegradedThreshold;
_unhealthyThreshold = threshold.UnhealthyThreshold;
}

protected override string ConnectionKey => $"{Prefix}_{_queueName}";

/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
{
try
{
var managementClient = ManagementClientConnections.GetOrAdd(ConnectionKey, CreateManagementClient());

var properties = await managementClient.GetQueueRuntimePropertiesAsync(_queueName, cancellationToken).ConfigureAwait(false);

var messagesCount = GetMessagesCount(properties);

if (messagesCount >= _unhealthyThreshold)
{
return HealthCheckResult.Unhealthy($"Message in {GetQueueType()} {_queueName} exceeded the amount of messages allowed for the unhealthy threshold {_unhealthyThreshold}/{messagesCount}");
}

if (messagesCount >= _degradedThreshold)
{
return HealthCheckResult.Degraded($"Message in {GetQueueType()} {_queueName} exceeded the amount of messages allowed for the degraded threshold {_degradedThreshold}/{messagesCount}");
}

return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
}
}

private long GetMessagesCount(QueueRuntimeProperties queueRuntimeProperty) => _checkDeadLetterMessages
? queueRuntimeProperty.DeadLetterMessageCount
: queueRuntimeProperty.ActiveMessageCount;

private string GetQueueType() => _checkDeadLetterMessages
? "dead letter queue"
: "queue";

private static AzureServiceBusQueueMessagesCountThreshold GetThreshold(AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions options) =>
options.CheckDeadLetterMessages
? options.DeadLetterMessages
: options.ActiveMessages;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace HealthChecks.AzureServiceBus.Configuration;

/// <summary>
/// Threshold configuration options for <see cref="AzureServiceBusQueueMessageCountThresholdHealthCheck"/>.
/// </summary>
public struct AzureServiceBusQueueMessagesCountThreshold
{
/// <summary>
/// Number of active/dead letter Service Bus messages in the queue before message health check returned <see cref="HealthStatus.Degraded"/>.
/// </summary>
public int DegradedThreshold { get; set; } = 5;

/// <summary>
/// Number of active/dead letter Service Bus messages in the queue before message health check returned <see cref="HealthStatus.Unhealthy"/>.
/// </summary>
public int UnhealthyThreshold { get; set; } = 10;

public AzureServiceBusQueueMessagesCountThreshold()
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace HealthChecks.AzureServiceBus.Configuration;

/// <summary>
/// Configuration options for <see cref="AzureServiceBusQueueMessageCountThresholdHealthCheck"/>.
/// </summary>
public class AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions : AzureServiceBusQueueHealthCheckOptions
{
/// <summary>
/// Indicates if dead letter messages queue or active messages queue should be checked.
/// </summary>
public bool CheckDeadLetterMessages { get; }
cieciurm marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Threshold configuration for active messages queue.
/// </summary>
public AzureServiceBusQueueMessagesCountThreshold ActiveMessages { get; set; }

/// <summary>
/// Threshold configuration for dead letter messages queue.
/// </summary>
public AzureServiceBusQueueMessagesCountThreshold DeadLetterMessages { get; set; }
cieciurm marked this conversation as resolved.
Show resolved Hide resolved

public AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions(string queueName, bool checkDeadLetterMessages, AzureServiceBusQueueMessagesCountThreshold threshold)
: base(queueName)
{
CheckDeadLetterMessages = checkDeadLetterMessages;
ActiveMessages = threshold;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Extension methods to configure <see cref="AzureEventHubHealthCheck"/>,
/// <see cref="AzureServiceBusHealthCheck{TOptions}"/>, <see cref="AzureServiceBusQueueHealthCheck"/>,
/// <see cref="AzureServiceBusSubscriptionHealthCheck"/>, <see cref="AzureServiceBusTopicHealthCheck"/>.
/// <see cref="AzureServiceBusSubscriptionHealthCheck"/>, <see cref="AzureServiceBusTopicHealthCheck"/>,
/// <see cref="AzureServiceBusQueueMessageCountThresholdHealthCheck"/>.
/// </summary>
public static class AzureServiceBusHealthCheckBuilderExtensions
{
private const string AZUREEVENTHUB_NAME = "azureeventhub";
private const string AZUREQUEUE_NAME = "azurequeue";
private const string AZURETOPIC_NAME = "azuretopic";
private const string AZURESUBSCRIPTION_NAME = "azuresubscription";
private const string AZUREQUEUETHRESHOLD_NAME = "azurequeuethreshold";
cieciurm marked this conversation as resolved.
Show resolved Hide resolved
private const string AZUREDEADLETTERQUEUETHRESHOLD_NAME = "azuredeadletterqueuethreshold";

/// <summary>
/// Add a health check for specified Azure Event Hub.
Expand Down Expand Up @@ -384,6 +387,204 @@ public static IHealthChecksBuilder AddAzureServiceBusQueue(
timeout));
}

/// <summary>
/// Add a health check for specified Azure Service Bus Queue message threshold
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="connectionString">The azure service bus connection string to be used.</param>
/// <param name="queueName">The name of the queue to check.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'azurequeue' will be used for the name.</param>
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="configure">An optional action to allow additional Azure Service Bus configuration.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAzureServiceBusQueueMessageCountThreshold(
this IHealthChecksBuilder builder,
string connectionString,
string queueName,
string? name = default,
Action<AzureServiceBusQueueMessagesCountThreshold>? configure = null,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
Guard.ThrowIfNull(connectionString);
Guard.ThrowIfNull(queueName);

var threshold = new AzureServiceBusQueueMessagesCountThreshold();
configure?.Invoke(threshold);

var options = new AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions(
queueName,
checkDeadLetterMessages: true,
threshold)
{
ConnectionString = connectionString,
};

return builder.Add(new HealthCheckRegistration(
name ?? AZUREQUEUETHRESHOLD_NAME,
sp => new AzureServiceBusQueueMessageCountThresholdHealthCheck(options),
failureStatus,
tags,
timeout));
}

/// <summary>
/// Add a health check for specified Azure Service Bus Queue message threshold
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="endpoint">The azure service bus endpoint to be used, format sb://myservicebus.servicebus.windows.net/.</param>
/// <param name="queueName">The name of the queue to check.</param>
/// <param name="tokenCredential">The token credential for auth</param>
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="configure">An optional action to allow additional Azure Service Bus configuration.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'azurequeue' will be used for the name.</param>
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAzureServiceBusQueueMessageCountThreshold(
this IHealthChecksBuilder builder,
string endpoint,
string queueName,
TokenCredential tokenCredential,
Action<AzureServiceBusQueueMessagesCountThreshold>? configure = null,
string? name = default,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
Guard.ThrowIfNull(endpoint);
Guard.ThrowIfNull(queueName);
Guard.ThrowIfNull(tokenCredential);

var threshold = new AzureServiceBusQueueMessagesCountThreshold();
configure?.Invoke(threshold);

var options = new AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions(
queueName,
checkDeadLetterMessages: false,
threshold)
{
FullyQualifiedNamespace = endpoint,
Credential = tokenCredential,
};

return builder.Add(new HealthCheckRegistration(
name ?? AZUREQUEUETHRESHOLD_NAME,
sp => new AzureServiceBusQueueMessageCountThresholdHealthCheck(options),
failureStatus,
tags,
timeout));
}

/// <summary>
/// Add a health check for specified Azure Service Bus Dead letter Queue message threshold
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="connectionString">The azure service bus connection string to be used.</param>
/// <param name="queueName">The name of the queue to check.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'azurequeue' will be used for the name.</param>
/// <param name="configure">An optional action to allow additional Azure Service Bus configuration.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAzureServiceBusDeadLetterQueueMessageCountThreshold(
this IHealthChecksBuilder builder,
string connectionString,
string queueName,
Action<AzureServiceBusQueueMessagesCountThreshold>? configure = null,
string? name = default,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
Guard.ThrowIfNull(connectionString);
Guard.ThrowIfNull(queueName);

var threshold = new AzureServiceBusQueueMessagesCountThreshold();

configure?.Invoke(threshold);

var options = new AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions(
queueName,
checkDeadLetterMessages: true,
threshold)
{
ConnectionString = connectionString,
};

return builder.Add(new HealthCheckRegistration(
name ?? AZUREDEADLETTERQUEUETHRESHOLD_NAME,
sp => new AzureServiceBusQueueMessageCountThresholdHealthCheck(options),
failureStatus,
tags,
timeout));
}

/// <summary>
/// Add a health check for specified Azure Service Bus Dead letter Queue message threshold
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="endpoint">The azure service bus endpoint to be used, format sb://myservicebus.servicebus.windows.net/.</param>
/// <param name="queueName">The name of the queue to check.</param>
/// <param name="tokenCredential">The token credential for auth</param>
/// <param name="configure">An optional action to allow additional Azure Service Bus configuration.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'azurequeue' will be used for the name.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAzureServiceBusDeadLetterQueueMessageCountThreshold(
this IHealthChecksBuilder builder,
string endpoint,
string queueName,
TokenCredential tokenCredential,
Action<AzureServiceBusQueueMessagesCountThreshold>? configure = null,
string? name = default,
HealthStatus? failureStatus = default,
IEnumerable<string>? tags = default,
TimeSpan? timeout = default)
{
Guard.ThrowIfNull(endpoint);
Guard.ThrowIfNull(queueName);
Guard.ThrowIfNull(tokenCredential);

var threshold = new AzureServiceBusQueueMessagesCountThreshold();

configure?.Invoke(threshold);

var options = new AzureServiceBusQueueMessagesCountThresholdHealthCheckOptions(
queueName,
checkDeadLetterMessages: true,
threshold)
{
FullyQualifiedNamespace = endpoint,
Credential = tokenCredential,
};

return builder.Add(new HealthCheckRegistration(
name ?? AZUREDEADLETTERQUEUETHRESHOLD_NAME,
sp => new AzureServiceBusQueueMessageCountThresholdHealthCheck(options),
failureStatus,
tags,
timeout));
}

/// <summary>
/// Add a health check for Azure Service Bus Topic.
/// </summary>
Expand Down
Loading