Skip to content

Commit

Permalink
Xabaril#2205 capture about db health check types
Browse files Browse the repository at this point in the history
  • Loading branch information
thompson-tomo committed Jun 1, 2024
1 parent 2f6a10f commit 364c312
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 36 deletions.
13 changes: 10 additions & 3 deletions src/HealthChecks.ArangoDb/ArangoDbHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using ArangoDBNetStandard;
using ArangoDBNetStandard.AuthApi;
using ArangoDBNetStandard.Transport.Http;
Expand All @@ -8,6 +9,10 @@ namespace HealthChecks.ArangoDb;
public class ArangoDbHealthCheck : IHealthCheck
{
private readonly ArangoDbOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(ArangoDbHealthCheck) },
{ "db.system", "arango" }
};

public ArangoDbHealthCheck(ArangoDbOptions options)
{
Expand All @@ -17,18 +22,20 @@ public ArangoDbHealthCheck(ArangoDbOptions options)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
using var transport = await GetTransportAsync(_options).ConfigureAwait(false);

using var adb = new ArangoDBClient(transport);
var databases = await adb.Database.GetCurrentDatabaseInfoAsync(cancellationToken).ConfigureAwait(false);
return databases.Error
? new HealthCheckResult(context.Registration.FailureStatus, $"HealthCheck failed with status code: {databases.Code}.")
: HealthCheckResult.Healthy();
? new HealthCheckResult(context.Registration.FailureStatus, $"HealthCheck failed with status code: {databases.Code}.", data: new ReadOnlyDictionary<string, object>(checkDetails))
: HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails));
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, ex.Message, ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/HealthChecks.DocumentDb/DocumentDbHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Diagnostics.HealthChecks;

Expand All @@ -8,6 +9,10 @@ public class DocumentDbHealthCheck : IHealthCheck
{
private static readonly ConcurrentDictionary<string, DocumentClient> _connections = new();
private readonly DocumentDbOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(DocumentDbHealthCheck) },
{ "db.system", "documentdb" }
};

public DocumentDbHealthCheck(DocumentDbOptions documentDbOptions)
{
Expand All @@ -20,10 +25,14 @@ public DocumentDbHealthCheck(DocumentDbOptions documentDbOptions)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
checkDetails.Add("db.namespace", _options.DatabaseName ?? "");
checkDetails.Add("db.collection.name", _options.CollectionName ?? "");
if (!_connections.TryGetValue(_options.UriEndpoint, out var documentDbClient))
{
checkDetails.Add("server.address", _options.UriEndpoint);
documentDbClient = new DocumentClient(new Uri(_options.UriEndpoint), _options.PrimaryKey);

if (!_connections.TryAdd(_options.UriEndpoint, documentDbClient))
Expand All @@ -42,11 +51,11 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
await documentDbClient.OpenAsync(cancellationToken).ConfigureAwait(false);
}

return HealthCheckResult.Healthy();
return HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails));
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
}
10 changes: 8 additions & 2 deletions src/HealthChecks.DynamoDb/DynamoDbHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
Expand All @@ -11,6 +12,10 @@ namespace HealthChecks.DynamoDb;
public class DynamoDbHealthCheck : IHealthCheck
{
private readonly DynamoDBOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(DynamoDbHealthCheck) },
{ "db.system", "dynamodb" }
};

/// <summary>
/// Creates health check for AWS DynamoDb database with the specified options.
Expand All @@ -24,6 +29,7 @@ public DynamoDbHealthCheck(DynamoDBOptions options)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
AWSCredentials? credentials = _options.Credentials;
Expand All @@ -50,11 +56,11 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context

var response = await client.ListTablesAsync(request, cancellationToken).ConfigureAwait(false);

return HealthCheckResult.Healthy();
return HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails));
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}

Expand Down
21 changes: 14 additions & 7 deletions src/HealthChecks.Elasticsearch/ElasticsearchHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using Elasticsearch.Net;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Nest;
Expand All @@ -10,6 +11,10 @@ public class ElasticsearchHealthCheck : IHealthCheck
private static readonly ConcurrentDictionary<string, ElasticClient> _connections = new();

private readonly ElasticsearchOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(ElasticsearchHealthCheck) },
{ "db.system", "elasticsearch" }
};

public ElasticsearchHealthCheck(ElasticsearchOptions options)
{
Expand All @@ -19,6 +24,7 @@ public ElasticsearchHealthCheck(ElasticsearchOptions options)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
if (!_connections.TryGetValue(_options.Uri, out var lowLevelClient))
Expand Down Expand Up @@ -52,6 +58,7 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context

if (!_connections.TryAdd(_options.Uri, lowLevelClient))
{
checkDetails.Add("server.address", _options.Uri);
lowLevelClient = _connections[_options.Uri];
}
}
Expand All @@ -62,27 +69,27 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context

if (healthResponse.ApiCall.HttpStatusCode != 200)
{
return new HealthCheckResult(context.Registration.FailureStatus);
return new HealthCheckResult(context.Registration.FailureStatus, data: new ReadOnlyDictionary<string, object>(checkDetails));
}

return healthResponse.Status switch
{
Health.Green => HealthCheckResult.Healthy(),
Health.Yellow => HealthCheckResult.Degraded(),
_ => new HealthCheckResult(context.Registration.FailureStatus)
Health.Green => HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails)),
Health.Yellow => HealthCheckResult.Degraded(data: new ReadOnlyDictionary<string, object>(checkDetails)),
_ => new HealthCheckResult(context.Registration.FailureStatus, data: new ReadOnlyDictionary<string, object>(checkDetails))
};
}

var pingResult = await lowLevelClient.PingAsync(ct: cancellationToken).ConfigureAwait(false);
bool isSuccess = pingResult.ApiCall.HttpStatusCode == 200;

return isSuccess
? HealthCheckResult.Healthy()
: new HealthCheckResult(context.Registration.FailureStatus);
? HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails))
: new HealthCheckResult(context.Registration.FailureStatus, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
}
14 changes: 10 additions & 4 deletions src/HealthChecks.InfluxDB/InfluxDBHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using Microsoft.Extensions.Diagnostics.HealthChecks;
Expand All @@ -10,6 +11,10 @@ public class InfluxDBHealthCheck : IHealthCheck, IDisposable
private readonly InfluxDBClient _influxDbClient;
#pragma warning restore IDISP008 // Don't assign member with injected and created disposables
private readonly bool _ownsClient;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(InfluxDBHealthCheck) },
{ "db.system", "influxdb" }
};

public InfluxDBHealthCheck(Func<InfluxDBClientOptions.Builder, InfluxDBClientOptions> _options)
{
Expand All @@ -26,6 +31,7 @@ public InfluxDBHealthCheck(InfluxDBClient influxDBClient)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
var ready = await _influxDbClient.ReadyAsync().ConfigureAwait(false);
Expand All @@ -35,17 +41,17 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
{
var me = await _influxDbClient.GetUsersApi().MeAsync(cancellationToken).ConfigureAwait(false);
return me?.Status == User.StatusEnum.Active
? HealthCheckResult.Healthy($"Started:{ready.Started} Up:{ready.Up}")
: HealthCheckResult.Degraded($"User status is {me?.Status}.");
? HealthCheckResult.Healthy($"Started:{ready.Started} Up:{ready.Up}", data: new ReadOnlyDictionary<string, object>(checkDetails))
: HealthCheckResult.Degraded($"User status is {me?.Status}.", data: new ReadOnlyDictionary<string, object>(checkDetails));
}
else
{
return HealthCheckResult.Unhealthy($"Ping:{ping} Status:{ready.Status} Started:{ready.Started} Up:{ready.Up}");
return HealthCheckResult.Unhealthy($"Ping:{ping} Status:{ready.Status} Started:{ready.Started} Up:{ready.Up}", data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy(ex.Message, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, ex.Message, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/HealthChecks.MongoDb/MongoDbHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using MongoDB.Bson;
using MongoDB.Driver;
Expand All @@ -11,6 +12,10 @@ public class MongoDbHealthCheck : IHealthCheck
private static readonly ConcurrentDictionary<string, IMongoClient> _mongoClient = new();
private readonly MongoClientSettings _mongoClientSettings;
private readonly string? _specifiedDatabase;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(MongoDbHealthCheck) },
{ "db.system", "mongodb" }
};

public MongoDbHealthCheck(string connectionString, string? databaseName = default)
: this(MongoClientSettings.FromUrl(MongoUrl.Create(connectionString)), databaseName)
Expand All @@ -36,12 +41,16 @@ public MongoDbHealthCheck(MongoClientSettings clientSettings, string? databaseNa
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
var mongoClient = _mongoClient.GetOrAdd(_mongoClientSettings.ToString(), _ => new MongoClient(_mongoClientSettings));
checkDetails.Add("server.address", _mongoClientSettings.Server.Host);
checkDetails.Add("server.port", _mongoClientSettings.Server.Port);

if (!string.IsNullOrEmpty(_specifiedDatabase))
{
checkDetails.Add("db.namespace", _specifiedDatabase);
// some users can't list all databases depending on database privileges, with
// this you can check a specified database.
// Related with issue #43 and #617
Expand All @@ -57,11 +66,11 @@ await mongoClient
await cursor.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
}

return HealthCheckResult.Healthy();
return HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails));
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
}
13 changes: 11 additions & 2 deletions src/HealthChecks.MySql/MySqlHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using MySqlConnector;

Expand All @@ -9,6 +10,10 @@ namespace HealthChecks.MySql;
public class MySqlHealthCheck : IHealthCheck
{
private readonly MySqlHealthCheckOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(MySqlHealthCheck) },
{ "db.system", "mysql" }
};

public MySqlHealthCheck(MySqlHealthCheckOptions options)
{
Expand All @@ -18,11 +23,14 @@ public MySqlHealthCheck(MySqlHealthCheckOptions options)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
using var connection = _options.DataSource is not null ?
_options.DataSource.CreateConnection() :
new MySqlConnection(_options.ConnectionString);
checkDetails.Add("db.namespace", connection.Database);
checkDetails.Add("server.address", connection.DataSource);

_options.Configure?.Invoke(connection);
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
Expand All @@ -31,6 +39,7 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
{
using var command = connection.CreateCommand();
command.CommandText = _options.CommandText;
checkDetails.Add("db.query.text", _options.CommandText);
object? result = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);

return _options.HealthCheckResultBuilder == null
Expand All @@ -41,13 +50,13 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
{
var success = await connection.PingAsync(cancellationToken).ConfigureAwait(false);
return _options.HealthCheckResultBuilder is null
? (success ? HealthCheckResult.Healthy() : new HealthCheckResult(context.Registration.FailureStatus)) :
? (success ? HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails)) : new HealthCheckResult(context.Registration.FailureStatus, data: new ReadOnlyDictionary<string, object>(checkDetails))) :
_options.HealthCheckResultBuilder(success);
}
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
}
13 changes: 11 additions & 2 deletions src/HealthChecks.NpgSql/NpgSqlHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Npgsql;
Expand All @@ -10,6 +11,10 @@ namespace HealthChecks.NpgSql;
public class NpgSqlHealthCheck : IHealthCheck
{
private readonly NpgSqlHealthCheckOptions _options;
private readonly Dictionary<string, object> _baseCheckDetails = new Dictionary<string, object>{
{ "healthcheck.type", nameof(NpgSqlHealthCheck) },
{ "db.system", "npgsql" }
};

public NpgSqlHealthCheck(NpgSqlHealthCheckOptions options)
{
Expand All @@ -21,12 +26,16 @@ public NpgSqlHealthCheck(NpgSqlHealthCheckOptions options)
/// <inheritdoc />
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> checkDetails = _baseCheckDetails;
try
{
await using var connection = _options.DataSource is not null
? _options.DataSource.CreateConnection()
: new NpgsqlConnection(_options.ConnectionString);

checkDetails.Add("db.query.text", _options.CommandText);
checkDetails.Add("db.namespace", connection.Database);
checkDetails.Add("server.address", connection.DataSource);
_options.Configure?.Invoke(connection);
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);

Expand All @@ -35,12 +44,12 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
var result = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);

return _options.HealthCheckResultBuilder == null
? HealthCheckResult.Healthy()
? HealthCheckResult.Healthy(data: new ReadOnlyDictionary<string, object>(checkDetails))
: _options.HealthCheckResultBuilder(result);
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, description: ex.Message, exception: ex);
return new HealthCheckResult(context.Registration.FailureStatus, description: ex.Message, exception: ex, data: new ReadOnlyDictionary<string, object>(checkDetails));
}
}
}
Loading

0 comments on commit 364c312

Please sign in to comment.