EF Core 与 MySQL:日志和调试详解
<p>本文将详细讲解EF Core与MySQL的日志和调试,分为三个部分:EF Core日志配置、MySQL日志查看以及使用调试工具分析查询性能。</p><h2>1. 配置 EF Core 日志</h2>
<h3>基本日志配置</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在DbContext配置中启用日志记录</span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> connectionString = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">server=localhost;database=efcoredb;user=root;password=yourpassword</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
.EnableSensitiveDataLogging() </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录参数值(仅开发环境)</span>
.EnableDetailedErrors() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提供更详细的错误信息(仅开发环境)</span>
.LogTo(Console.WriteLine, LogLevel.Information) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出到控制台</span>
<span style="color: rgba(0, 0, 0, 1)"> .LogTo(
message </span>=> Debug.WriteLine(message), <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出到调试输出</span>
<span style="color: rgba(0, 0, 0, 1)"> LogLevel.Debug
);
}</span></pre>
</div>
<h3>使用 ILoggerFactory(ASP.NET Core 应用)</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在Program.cs或Startup.cs中配置</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> ConfigureServices(IServiceCollection services)
{
services.AddDbContext</span><ApplicationDbContext>(options =><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> connectionString = Configuration.GetConnectionString(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">DefaultConnection</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
.EnableSensitiveDataLogging(Environment.IsDevelopment())
.EnableDetailedErrors(Environment.IsDevelopment())
.UseLoggerFactory(LoggerFactory.Create(builder </span>=><span style="color: rgba(0, 0, 0, 1)">
{
builder
.AddConsole()
.AddDebug()
.AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Information);
}));
});
}</span></pre>
</div>
<h3>自定义日志过滤器</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 只记录执行时间超过100ms的查询</span>
<span style="color: rgba(0, 0, 0, 1)">optionsBuilder.LogTo(
message </span>=><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (message.Contains(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CommandExecuted</span><span style="color: rgba(128, 0, 0, 1)">"</span>) && message.Contains(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Elapsed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">))
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> elapsedMatch = Regex.Match(message, <span style="color: rgba(128, 0, 0, 1)">@"</span><span style="color: rgba(128, 0, 0, 1)">Elapsed:(\d+)ms</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (elapsedMatch.Success && <span style="color: rgba(0, 0, 255, 1)">int</span>.Parse(elapsedMatch.Groups[<span style="color: rgba(128, 0, 128, 1)">1</span>].Value) > <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">)
{
Console.WriteLine($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">慢查询: {message}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
},
LogLevel.Information
);</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>结构化日志记录</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用Serilog等结构化日志框架</span>
Log.Logger = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console(outputTemplate: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.WriteTo.File(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">logs/efcore-.txt</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, rollingInterval: RollingInterval.Day)
.CreateLogger();
services.AddDbContext</span><ApplicationDbContext>(options =><span style="color: rgba(0, 0, 0, 1)">
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
.LogTo(Log.Logger.Information, LogLevel.Information)
.LogTo(Log.Logger.Warning, LogLevel.Warning)
.LogTo(Log.Logger.Error, LogLevel.Error);
});</span></pre>
</div>
<h2>2. 查看 MySQL 日志</h2>
<h3>启用和配置 MySQL 日志</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 查看当前日志配置</span>
SHOW VARIABLES <span style="color: rgba(128, 128, 128, 1)">LIKE</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">%log%</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 启用通用查询日志(记录所有查询)</span>
<span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL general_log <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">ON</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL general_log_file <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">/var/log/mysql/general.log</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 启用慢查询日志</span>
<span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL slow_query_log <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">ON</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL slow_query_log_file <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">/var/log/mysql/slow.log</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL long_query_time <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>; <span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 超过1秒的查询视为慢查询</span>
<span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 启用二进制日志(用于复制和恢复)</span>
<span style="color: rgba(0, 0, 255, 1)">SET</span> GLOBAL log_bin <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">ON</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 启用错误日志(通常默认开启)</span>
SHOW VARIABLES <span style="color: rgba(128, 128, 128, 1)">LIKE</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">log_error</span><span style="color: rgba(255, 0, 0, 1)">'</span>;</pre>
</div>
<h3>在 MySQL 配置文件中设置日志</h3>
<div class="cnblogs_code">
<pre># /etc/mysql/my.cnf 或 /etc/mysql/mysql.conf.d/<span style="color: rgba(0, 0, 0, 1)">mysqld.cnf
# 通用查询日志
general_log </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
general_log_file </span>= /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">general.log
# 慢查询日志
slow_query_log </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
slow_query_log_file </span>= /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">slow.log
long_query_time </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
log_queries_not_using_indexes </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
# 错误日志
log_error </span>= /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">error.log
# 二进制日志
log_bin </span>= /var/log/mysql/mysql-<span style="color: rgba(0, 0, 0, 1)">bin.log
expire_logs_days </span>= <span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">
max_binlog_size </span>= 100M</pre>
</div>
<h3>分析 MySQL 日志</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 查看慢查询日志
</span><span style="color: rgba(0, 0, 255, 1)">sudo</span> <span style="color: rgba(0, 0, 255, 1)">tail</span> -f /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">slow.log
# 使用mysqldumpslow分析慢查询日志
</span><span style="color: rgba(0, 0, 255, 1)">sudo</span> mysqldumpslow /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">slow.log
# 按执行时间排序查看最慢的查询
</span><span style="color: rgba(0, 0, 255, 1)">sudo</span> mysqldumpslow -s t /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">slow.log
# 查看通用查询日志
</span><span style="color: rgba(0, 0, 255, 1)">sudo</span> <span style="color: rgba(0, 0, 255, 1)">tail</span> -f /var/log/mysql/<span style="color: rgba(0, 0, 0, 1)">general.log
# 使用pt</span>-query-<span style="color: rgba(0, 0, 0, 1)">digest分析查询(需要安装Percona Toolkit)
</span><span style="color: rgba(0, 0, 255, 1)">sudo</span> pt-query-digest /var/log/mysql/slow.log</pre>
</div>
<h3>在应用程序中记录 MySQL 性能指标</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建性能监控服务</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MySqlPerformanceMonitor
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationDbContext _context;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger<MySqlPerformanceMonitor><span style="color: rgba(0, 0, 0, 1)"> _logger;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> MySqlPerformanceMonitor(ApplicationDbContext context, ILogger<MySqlPerformanceMonitor><span style="color: rgba(0, 0, 0, 1)"> logger)
{
_context </span>=<span style="color: rgba(0, 0, 0, 1)"> context;
_logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task MonitorQueryPerformanceAsync(Func<Task> databaseOperation, <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> operationName)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> stopwatch =<span style="color: rgba(0, 0, 0, 1)"> Stopwatch.StartNew();
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> databaseOperation();
stopwatch.Stop();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (stopwatch.ElapsedMilliseconds > <span style="color: rgba(128, 0, 128, 1)">1000</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 超过1秒的记录为警告</span>
<span style="color: rgba(0, 0, 0, 1)"> {
_logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">慢查询检测: {Operation} 耗时 {ElapsedMs}ms</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
operationName, stopwatch.ElapsedMilliseconds);
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (stopwatch.ElapsedMilliseconds > <span style="color: rgba(128, 0, 128, 1)">500</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 超过0.5秒的记录为信息</span>
<span style="color: rgba(0, 0, 0, 1)"> {
_logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">查询性能: {Operation} 耗时 {ElapsedMs}ms</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
operationName, stopwatch.ElapsedMilliseconds);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">查询执行失败: {Operation} 耗时 {ElapsedMs}ms</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
operationName, stopwatch.ElapsedMilliseconds);
</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
}</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h2>3. 使用调试工具分析查询性能</h2>
<h3>使用 EF Core 的诊断工具</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 启用诊断监听器</span>
DiagnosticListener.AllListeners.Subscribe(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> EfCoreDiagnosticListener());
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> EfCoreDiagnosticListener : IObserver<DiagnosticListener><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnNext(DiagnosticListener value)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (value.Name ==<span style="color: rgba(0, 0, 0, 1)"> DbLoggerCategory.Name)
{
value.Subscribe(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> EfCoreObserver());
}
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnCompleted() { }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnError(Exception error) { }
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> EfCoreObserver : IObserver<KeyValuePair<<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>>><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> OnNext(KeyValuePair<<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>><span style="color: rgba(0, 0, 0, 1)"> value)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (value.Key ==<span style="color: rgba(0, 0, 0, 1)"> RelationalEventId.CommandExecuted.Name)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> command = (IDbCommand)value.Value.GetType().GetProperty(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Command</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).GetValue(value.Value);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> duration = (TimeSpan)value.Value.GetType().GetProperty(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Duration</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).GetValue(value.Value);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (duration.TotalMilliseconds > <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">)
{
Debug.WriteLine($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">慢查询: {command.CommandText} 耗时: {duration.TotalMilliseconds}ms</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnCompleted() { }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnError(Exception error) { }
}</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>使用 Application Insights 监控</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在Startup.cs中配置Application Insights</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration[</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">APPLICATIONINSIGHTS_CONNECTION_STRING</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">]);
services.AddDbContext</span><ApplicationDbContext>(options =><span style="color: rgba(0, 0, 0, 1)">
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
.EnableSensitiveDataLogging()
.AddApplicationInsightsDiagnostics(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果支持的话</span>
<span style="color: rgba(0, 0, 0, 1)"> });
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者手动记录依赖跟踪</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<List<Product>><span style="color: rgba(0, 0, 0, 1)"> GetProductsAsync()
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> operation = telemetryClient.StartOperation<DependencyTelemetry>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MySQL Query</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
operation.Telemetry.Type </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MySQL</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
operation.Telemetry.Data </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SELECT * FROM Products</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> products = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _context.Products.ToListAsync();
operation.Telemetry.Success </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> products;
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
operation.Telemetry.Success </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
telemetryClient.TrackException(ex);
</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
telemetryClient.StopOperation(operation);
}
}</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>使用 MiniProfiler</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 安装MiniProfiler.EntityFrameworkCore
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在Startup.cs中配置</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> ConfigureServices(IServiceCollection services)
{
services.AddMiniProfiler(options </span>=><span style="color: rgba(0, 0, 0, 1)">
{
options.RouteBasePath </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/profiler</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
options.ColorScheme </span>=<span style="color: rgba(0, 0, 0, 1)"> StackExchange.Profiling.ColorScheme.Auto;
options.EnableMvcFilterProfiling </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
options.EnableMvcViewProfiling </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
options.TrackConnectionOpenClose </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}).AddEntityFramework();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiniProfiler();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ... 其他中间件</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在视图中添加MiniProfiler</span>
<span style="color: rgba(0, 0, 0, 1)">@inject StackExchange.Profiling.MiniProfiler Profiler
@MiniProfiler.RenderIncludes(Profiler)</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>使用 JetBrains dotTrace 或 dotMemory</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在代码中添加性能分析点</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<List<Product>><span style="color: rgba(0, 0, 0, 1)"> GetExpensiveProductsAsync()
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用JetBrains Profiler API(需要安装JetBrains.Profiler.Api包)</span>
<span style="color: rgba(0, 0, 0, 1)"> JetBrains.Profiler.Api.MeasureProfiler.StartCollectingData();
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> products = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _context.Products
.Where(p </span>=> p.Price > <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">)
.Include(p </span>=><span style="color: rgba(0, 0, 0, 1)"> p.Category)
.ToListAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> products;
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
JetBrains.Profiler.Api.MeasureProfiler.SaveData();
}
}</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>使用 MySQL EXPLAIN 分析查询计划</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在代码中执行EXPLAIN分析查询</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">string</span>><span style="color: rgba(0, 0, 0, 1)"> ExplainQueryAsync(IQueryable query)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> sql =<span style="color: rgba(0, 0, 0, 1)"> query.ToQueryString();
</span><span style="color: rgba(0, 0, 255, 1)">var</span> explainSql = $<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">EXPLAIN {sql}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> command =<span style="color: rgba(0, 0, 0, 1)"> _context.Database.GetDbConnection().CreateCommand())
{
command.CommandText </span>=<span style="color: rgba(0, 0, 0, 1)"> explainSql;
_context.Database.OpenConnection();
</span><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> result = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> command.ExecuteReaderAsync())
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> explanation = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringBuilder();
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> result.ReadAsync())
{
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < result.FieldCount; i++<span style="color: rgba(0, 0, 0, 1)">)
{
explanation.AppendLine($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{result.GetName(i)}: {result.GetValue(i)}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
explanation.AppendLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">---</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> explanation.ToString();
}
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用方式</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> query = _context.Products.Where(p => p.Price > <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> explanation = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> ExplainQueryAsync(query);
Console.WriteLine(explanation);</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>使用 Visual Studio 的调试工具</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在Visual Studio中使用诊断工具
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1. 打开"诊断工具"窗口(调试 -> 窗口 -> 显示诊断工具)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2. 开始调试并记录性能
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3. 分析CPU使用率、内存分配和数据库查询
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用条件断点调试特定查询</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> products =<span style="color: rgba(0, 0, 0, 1)"> _context.Products
.Where(p </span>=> p.Price > <span style="color: rgba(128, 0, 128, 1)">100</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在这里设置条件断点:p.Price > 1000</span>
<span style="color: rgba(0, 0, 0, 1)"> .ToList();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用即时窗口测试查询
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在调试期间,可以在即时窗口中执行:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> _context.Products.Where(p => p.Price > 100).ToList()</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>创建自定义性能分析中间件</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ASP.NET Core中间件,用于记录所有数据库操作</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> QueryPerformanceMiddleware
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> RequestDelegate _next;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger<QueryPerformanceMiddleware><span style="color: rgba(0, 0, 0, 1)"> _logger;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> QueryPerformanceMiddleware(RequestDelegate next, ILogger<QueryPerformanceMiddleware><span style="color: rgba(0, 0, 0, 1)"> logger)
{
_next </span>=<span style="color: rgba(0, 0, 0, 1)"> next;
_logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> Task InvokeAsync(HttpContext context, ApplicationDbContext dbContext)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> stopwatch =<span style="color: rgba(0, 0, 0, 1)"> Stopwatch.StartNew();
</span><span style="color: rgba(0, 0, 255, 1)">var</span> originalQueryTrackingBehavior =<span style="color: rgba(0, 0, 0, 1)"> dbContext.ChangeTracker.QueryTrackingBehavior;
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置为NoTracking以提高性能,除非需要修改数据</span>
dbContext.ChangeTracker.QueryTrackingBehavior =<span style="color: rgba(0, 0, 0, 1)"> QueryTrackingBehavior.NoTracking;
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _next(context);
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
stopwatch.Stop();
dbContext.ChangeTracker.QueryTrackingBehavior </span>=<span style="color: rgba(0, 0, 0, 1)"> originalQueryTrackingBehavior;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录请求处理时间和数据库查询统计</span>
_logger.LogInformation(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">请求 {Method} {Path} 处理时间: {ElapsedMs}ms, 数据库查询次数: {QueryCount}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
context.Request.Method, context.Request.Path, stopwatch.ElapsedMilliseconds,
dbContext.GetQueryCount());
}
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 扩展方法获取查询次数</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> DbContextExtensions
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">int</span> GetQueryCount(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> DbContext context)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这种方法可能不准确,更好的方式是使用诊断监听器</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实际实现需要更复杂的方法</span>
<span style="color: rgba(0, 0, 0, 1)"> }
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在Startup.cs中注册中间件</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware</span><QueryPerformanceMiddleware><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ... 其他中间件</span>
}</pre>
</div>
<h2>总结</h2>
<p class="ds-markdown-paragraph">本教程详细介绍了EF Core与MySQL的日志记录和调试技术,包括:</p>
<ol start="1">
<li>
<p class="ds-markdown-paragraph">配置EF Core日志:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">基本日志配置和敏感数据记录</p>
</li>
<li>
<p class="ds-markdown-paragraph">使用ILoggerFactory集成ASP.NET Core日志系统</p>
</li>
<li>
<p class="ds-markdown-paragraph">自定义日志过滤器和结构化日志记录</p>
</li>
</ul>
</li>
<li>
<p class="ds-markdown-paragraph">查看MySQL日志:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">启用和配置各种MySQL日志(通用查询、慢查询、错误日志)</p>
</li>
<li>
<p class="ds-markdown-paragraph">分析MySQL日志以识别性能问题</p>
</li>
<li>
<p class="ds-markdown-paragraph">在应用程序中集成MySQL性能监控</p>
</li>
</ul>
</li>
<li>
<p class="ds-markdown-paragraph">使用调试工具分析查询性能:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">EF Core诊断工具和监听器</p>
</li>
<li>
<p class="ds-markdown-paragraph">Application Insights集成</p>
</li>
<li>
<p class="ds-markdown-paragraph">MiniProfiler实时性能分析</p>
</li>
<li>
<p class="ds-markdown-paragraph">JetBrains专业分析工具</p>
</li>
<li>
<p class="ds-markdown-paragraph">MySQL EXPLAIN查询计划分析</p>
</li>
<li>
<p class="ds-markdown-paragraph">Visual Studio调试工具</p>
</li>
<li>
<p class="ds-markdown-paragraph">自定义性能分析中间件</p>
</li>
</ul>
</li>
</ol>
<p class="ds-markdown-paragraph">通过这些工具和技术,我们可以:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">识别和解决性能瓶颈</p>
</li>
<li>
<p class="ds-markdown-paragraph">优化数据库查询</p>
</li>
<li>
<p class="ds-markdown-paragraph">监控应用程序的健康状况</p>
</li>
<li>
<p class="ds-markdown-paragraph">诊断和解决生产环境中的问题</p>
</li>
</ul>
<p class="ds-markdown-paragraph">建议在开发环境中启用详细日志记录和性能分析,而在生产环境中则使用更保守的配置,只记录警告和错误,并定期检查慢查询日志以识别需要优化的查询。</p>
<p class="ds-markdown-paragraph"> </p>
<p class="ds-markdown-paragraph">EF Core系列的总结到这儿就告一段落了,后面有其他的知识点再补充,接下来计划开启RabbitMQ系列。</p><br><br>
来源:https://www.cnblogs.com/jixingsuiyuan/p/19099782
頁:
[1]