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