using System.Data.Common; using System.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; namespace Tiku.Infrastructure.Observability; public sealed class DatabasePerformanceInterceptor( ILogger 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 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 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 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); } } }