月未盈 發表於 2025-9-18 22:33:00

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>=&gt; 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&nbsp;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>&lt;ApplicationDbContext&gt;(options =&gt;<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>=&gt;<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>=&gt;<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>) &amp;&amp; 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 &amp;&amp; <span style="color: rgba(0, 0, 255, 1)">int</span>.Parse(elapsedMatch.Groups[<span style="color: rgba(128, 0, 128, 1)">1</span>].Value) &gt; <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>&lt;ApplicationDbContext&gt;(options =&gt;<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&lt;MySqlPerformanceMonitor&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> MySqlPerformanceMonitor(ApplicationDbContext context, ILogger&lt;MySqlPerformanceMonitor&gt;<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&lt;Task&gt; 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 &gt; <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 &gt; <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&lt;DiagnosticListener&gt;<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&lt;KeyValuePair&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;&gt;<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&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;<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 &gt; <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>&lt;ApplicationDbContext&gt;(options =&gt;<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&lt;List&lt;Product&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> GetProductsAsync()
{
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> operation = telemetryClient.StartOperation&lt;DependencyTelemetry&gt;(<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>=&gt;<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&lt;List&lt;Product&gt;&gt;<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>=&gt; p.Price &gt; <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">)
            .Include(p </span>=&gt;<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&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;<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 &lt; 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 =&gt; p.Price &gt; <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. 打开"诊断工具"窗口(调试 -&gt; 窗口 -&gt; 显示诊断工具)
</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>=&gt; p.Price &gt; <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 &gt; 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 =&gt; p.Price &gt; 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&lt;QueryPerformanceMiddleware&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> QueryPerformanceMiddleware(RequestDelegate next, ILogger&lt;QueryPerformanceMiddleware&gt;<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>&lt;QueryPerformanceMiddleware&gt;<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&nbsp;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">&nbsp;</p>
<p class="ds-markdown-paragraph">EF Core系列的总结到这儿就告一段落了,后面有其他的知识点再补充,接下来计划开启RabbitMQ系列。</p><br><br>
来源:https://www.cnblogs.com/jixingsuiyuan/p/19099782
頁: [1]
查看完整版本: EF Core 与 MySQL:日志和调试详解