Resumo : neste tutorial, você aprenderá como registrar consultas SQL no EF Core para fins aprimorados de depuração e solução de problemas.
Configurando o log do EF Core
Para registrar informações geradas pelo EF Core em um destino como o Console, um arquivo ou um banco de dados, você pode configurá-lo no OnConfiguring()
método da DbContext
classe assim:
protected override void OnConfiguring(
DbContextOptionsBuilder
optionsBuilder
)
{
optionsBuilder
.UseSqlServer(connectionString)
.LogTo(target)
}
Linguagem de código: C# ( cs )
Por exemplo, para registrar informações geradas pelo EF Core no Console, você pode passar the Console.WriteLine
para o OnConfiguring
método da seguinte maneira:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace HR;
public class HRContext : DbContext
{
public DbSet<Employee> Employees { get; set;}
public DbSet<Department> Departments { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("Local");
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine)
.EnableSensitiveDataLogging();
}
}
Linguagem de código: C# ( cs )
Se você executar o projeto, verá muitas informações exibidas no Console.
Filtrando os registros
O EF Core atribui a cada mensagem de log uma categoria e um nível de log. Para filtrar as mensagens, você pode especificar a categoria da mensagem e o nível de log usando o LogTo()
método.
Categoria de mensagem
O EF Core atribui uma categoria de mensagem a cada mensagem de log. Ao filtrar as mensagens por categorias, você pode especificar quais mensagens o EF Core deve incluir nos logs. Para fazer isso, você passa um array de categorias de mensagens para o LogTo()
método.
Por exemplo, o seguinte instrui o EF Core a incluir apenas comandos de banco de dados nos logs e gravá-los no Console:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("Local");
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name });
}
Linguagem de código: C# ( cs )
Se você executar o projeto, verá que o Console exibe apenas comandos de banco de dados.
Aqui está a versão extraída dos logs que mostra a consulta SQL que o EF Core enviou ao banco de dados para selecionar todas as linhas da Departments
tabela:
...
info: 6/8/2023 18:52:06.635 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (23ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [d].[Id], [d].[Name]
...
Linguagem de código: C# ( cs )
Nível de registro
Além da categoria de mensagem, o EF Core atribui um nível de log a cada mensagem de log. O nível de log é definido pelo LogLevel
enum com os seguintes membros:
- Depurar (padrão)
- Erro
- Crítico
- Informação
- Vestígio
- Aviso
- Nenhum
Por padrão, o EF Core inclui mensagens de log no nível de depuração e superior. Para filtrar mensagens de log, você pode passar um nível de log mais alto para o LogTo()
método.
Por exemplo, o seguinte grava apenas as mensagens de log de informações na Console:
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information);
Linguagem de código: C# ( cs )
Se você executar o programa, verá apenas as mensagens de registro de informações no console, como segue:
info: 6/8/2023 18:54:10.967 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (15ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [d].[Id], [d].[Name]
FROM [Departments] AS [d]
Linguagem de código: C# ( cs )
Habilitando dados confidenciais
Modifique no Program.cs
projeto do aplicativo HR Console para o seguinte:
using HR;
using static System.Console;
using var context = new HRContext();
var name = "Sales";
var departments = context.Departments
.Where(d => d.Name == name)
.ToList();
foreach (var department in departments)
{
WriteLine($"{department.Id} {department.Name}");
}
Linguagem de código: C# ( cs )
Neste programa encontramos os departamentos por nome e exibimos os resultados no console. Se você executar o programa, verá as seguintes informações de log:
info: 6/8/2023 18:55:01.286 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (36ms) [Parameters=[@__name_0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SELECT [d].[Id], [d].[Name]
FROM [Departments] AS [d]
WHERE [d].[Name] = @__name_0
Linguagem de código: C# ( cs )
Como você pode ver no log, ele não mostra o valor real do name
parâmetro. Em vez disso, mostra um ponto de interrogação ( ?
).
A razão é que os parâmetros de comando podem conter informações confidenciais, como senhas e números de segurança social. Se você armazenar informações confidenciais nos arquivos de log, elas se tornarão vulneráveis a várias formas de ataques, como hackers, violações de dados e roubo de identidade. Portanto, por padrão, o EF Core não inclui dados do aplicativo no log.
Se você trabalha no ambiente de desenvolvimento ou teste, talvez queira incluir os dados confidenciais no log para fins de depuração e teste.
Para fazer isso você pode chamar o EnableSensitiveDataLogging()
método do OptionBuilder
objeto assim:
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information)
.EnableSensitiveDataLogging();
Linguagem de código: C# ( cs )
E se você executar o aplicativo, verá o valor do nome ( 'Sales'
) em vez de um ponto de interrogação ( ?
) no log:
info: 6/8/2023 11:56:24.939 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (28ms) [Parameters=[@__name_0='Sales' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SELECT [d].[Id], [d].[Name]
FROM [Departments] AS [d]
WHERE [d].[Name] = @__name_0
Linguagem de código: C# ( cs )
Configurando destinos de criação de log
O EF Core dá suporte ao log nos seguintes destinos:
- Console
- Arquivo via
StreamWriter
- Janela de depuração
- Ou API de registrador externo
Por exemplo, você pode definir o destino de registro para um logs.txt
arquivo usando o StreamWriter
seguinte:
using HR.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace HR.Data;
public class HRContext : DbContext
{
// Write logs to logs.txt
StreamWriter _writer = new("logs.txt", true);
public DbSet<Employee> Employees { get; set;}
public DbSet<Department> Departments { get; set; }
protected override void OnConfiguring(
DbContextOptionsBuilder
optionsBuilder
)
{
// Pass the writer.WriterLine to
optionsBuilder
.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=HR")
.LogTo(_writer.WriteLine);
}
public override void Dispose()
{
// dispose the StreamWriter to save the log file
_writer.Dispose();
base.Dispose();
}
}
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, crie um novo StreamWriter
objeto que anexe mensagens de log ao logs.txt
arquivo:
StreamWriter _writer = new("logs.txt", true);
Linguagem de código: C# ( cs )
Observe que para anexar mensagens de log ao logs.txt
arquivo “”, você define o segundo argumento de como StreamWriter
verdadeiro. Caso contrário, substituirá os logs existentes.
Segundo, passe the _writer.WriteLine
como alvo para o LogTo()
método do OptionBuilder
objeto:
optionsBuilder.UseSqlServer(connectionString)
.LogTo(_writer.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information)
.EnableSensitiveDataLogging();
Linguagem de código: C# ( cs )
Terceiro, substitua o Dispose()
método da HRContext
classe para descartar o StreamWriter
objeto e garantir que o arquivo de log seja salvo corretamente:
public override void Dispose()
{
// dispose the StreamWriter to save the log file
_writer.Dispose();
base.Dispose();
}
Linguagem de código: C# ( cs )
Se você executar o programa, verá as mensagens de log no logs.txt
arquivo.
Observe que se você vir um arquivo logs.txt vazio, é provável que o objeto HRContext não esteja descartado corretamente. Nesse caso, você pode corrigir isso adicionando a instrução using nas declarações HRContext:
using HR;
using static System.Console;
using var context = new HRContext();
var name = "Sales";
var departments = context.Departments
.Where(d => d.Name == name)
.ToList();
foreach (var department in departments)
{
WriteLine($"{department.Id} {department.Name}");
}
Linguagem de código: JavaScript ( javascript )
Resumo
- Os logs são úteis para depuração e solução de problemas.
- Os logs do EF Core fornecem muitas informações, incluindo consultas SQL.
- Use a API de registro simples (
LogTo()
método) para configurar o registro em log do EF Core. - Use nível e categoria de log para filtrar as mensagens de log.
- Configure explicitamente dados confidenciais em parâmetros para incluir no log chamando o
EnableSensitiveDataLogging()
método doOptionBuilderobject
.