Files
tiku-backend.net/Tiku.Infrastructure/Observability/DatabasePerformanceInterceptor.cs

84 lines
2.8 KiB
C#

using System.Data.Common;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
namespace Tiku.Infrastructure.Observability;
public sealed class DatabasePerformanceInterceptor(
ILogger<DatabasePerformanceInterceptor> logger) : DbCommandInterceptor
{
private const double SlowCommandMilliseconds = 500;
public override DbDataReader ReaderExecuted(
DbCommand command,
CommandExecutedEventData eventData,
DbDataReader result)
{
Record(command, eventData.Duration, "reader");
return result;
}
public override ValueTask<DbDataReader> ReaderExecutedAsync(
DbCommand command,
CommandExecutedEventData eventData,
DbDataReader result,
CancellationToken cancellationToken = default)
{
Record(command, eventData.Duration, "reader");
return ValueTask.FromResult(result);
}
public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
{
Record(command, eventData.Duration, "non_query");
return result;
}
public override ValueTask<int> NonQueryExecutedAsync(
DbCommand command,
CommandExecutedEventData eventData,
int result,
CancellationToken cancellationToken = default)
{
Record(command, eventData.Duration, "non_query");
return ValueTask.FromResult(result);
}
public override object? ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object? result)
{
Record(command, eventData.Duration, "scalar");
return result;
}
public override ValueTask<object?> ScalarExecutedAsync(
DbCommand command,
CommandExecutedEventData eventData,
object? result,
CancellationToken cancellationToken = default)
{
Record(command, eventData.Duration, "scalar");
return ValueTask.FromResult(result);
}
private void Record(DbCommand command, TimeSpan duration, string operation)
{
var fingerprint = DatabasePerformanceTelemetry.Fingerprint(command.CommandText);
var tags = new TagList { { "db.operation", operation } };
DatabasePerformanceTelemetry.CommandCounter.Add(1, tags);
DatabasePerformanceTelemetry.CommandDuration.Record(
duration.TotalMilliseconds,
tags);
DatabaseRequestMetrics.Record(duration, fingerprint);
if (duration.TotalMilliseconds >= SlowCommandMilliseconds)
{
logger.LogWarning(
"Slow database command {CommandFingerprint} ({Operation}) completed in {ElapsedMilliseconds:F1} ms.",
fingerprint,
operation,
duration.TotalMilliseconds);
}
}
}