forked from xiongyuxing/tiku-backend.net
84 lines
2.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|