Log simples

Observação

Esse recurso foi introduzido no EF Core 5,0.

Dica

Você pode baixar o exemplo deste artigo de github.

O log simples de Entity Framework Core (EF Core) pode ser usado para obter logs facilmente ao desenvolver e depurar aplicativos. essa forma de registro em log requer configuração mínima e nenhum pacote de NuGet adicional.

Dica

O EF Core também se integra com o Microsoft. Extensions. Logging, que requer mais configuração, mas geralmente é mais adequado para o registro em log em aplicativos de produção.

Configuração

EF Core logs podem ser acessados de qualquer tipo de aplicativo por meio do uso de LogTo ao Configurar uma instância de DbContext. Essa configuração é normalmente feita em uma substituição de DbContext.OnConfiguring . Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

Como alternativa, LogTo pode ser chamado como parte de AddDbContext ou ao criar uma DbContextOptions instância para passar para o DbContext Construtor.

Dica

Onconfiguring ainda é chamado quando AddDbContext é usado ou uma instância de DbContextOptions é passada para o Construtor DbContext. Isso o torna o lugar ideal para aplicar a configuração de contexto, independentemente de como o DbContext é construído.

Direcionando os logs

Registrando em log no console

LogTo requer um Action<T> delegado que aceite uma cadeia de caracteres. EF Core chamará esse delegado com uma cadeia de caracteres para cada mensagem de log gerada. Em seguida, é até o delegado fazer algo com a mensagem determinada.

O Console.WriteLine método é geralmente usado para esse delegado, como mostrado acima. Isso resulta na gravação de cada mensagem de log no console.

Registrando em log na janela de depuração

Debug.WriteLinepode ser usado para enviar a saída para a janela de depuração no Visual Studio ou em outros IDEs. A sintaxe lambda deve ser usada nesse caso porque a Debug classe é compilada fora das compilações de versão. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(message => Debug.WriteLine(message));

Registrando em log em um arquivo

Gravar em um arquivo requer a criação de um StreamWriter ou semelhante para o arquivo. O WriteLine método pode ser usado como nos outros exemplos acima. Lembre-se de garantir que o arquivo seja fechado corretamente descartando o gravador quando o contexto for descartado. Por exemplo:

private readonly StreamWriter _logStream = new StreamWriter("mylog.txt", append: true);

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(_logStream.WriteLine);

public override void Dispose()
{
    base.Dispose();
    _logStream.Dispose();
}

public override async ValueTask DisposeAsync()
{
    await base.DisposeAsync();
    await _logStream.DisposeAsync();
}

Dica

Considere o uso de Microsoft. Extensions. Logging para registro em log em arquivos em aplicativos de produção.

Obtendo mensagens detalhadas

Dados Confidenciais

Por padrão, EF Core não incluirá os valores de quaisquer dados em mensagens de exceção. Isso ocorre porque esses dados podem ser confidenciais e podem ser revelados no uso de produção se uma exceção não for tratada.

No entanto, conhecer os valores de dados, especialmente para chaves, pode ser muito útil durante a depuração. Isso pode ser habilitado no EF Core chamando EnableSensitiveDataLogging() . Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableSensitiveDataLogging();

Exceções de consulta detalhadas

Por motivos de desempenho, EF Core não encapsula cada chamada para ler um valor do provedor de banco de dados em um bloco try-catch. No entanto, isso às vezes resulta em exceções que são difíceis de diagnosticar, especialmente quando o banco de dados retorna um valor nulo quando não é permitido pelo modelo.

Ativar EnableDetailedErrors fará com que o EF apresente esses blocos try-catch e, portanto, forneça erros mais detalhados. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableDetailedErrors();

Filtragem

Níveis de log

Cada mensagem de log de EF Core é atribuída a um nível definido pela LogLevel enumeração. Por padrão, EF Core log simples inclui cada mensagem no Debug nível ou acima. LogTo pode ser passado um nível mínimo mais alto para filtrar algumas mensagens. Por exemplo, passar Information resultados em um conjunto mínimo de logs limitado ao acesso ao banco de dados e a algumas mensagens de manutenção.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

Mensagens específicas

Cada mensagem de log é atribuída a um EventId . Essas IDs podem ser acessadas da CoreEventId classe ou da RelationalEventId classe de mensagens específicas relacionais. Um provedor de banco de dados também pode ter IDs específicas de provedor em uma classe semelhante. por exemplo, SqlServerEventId para o provedor de SQL Server.

LogTo pode ser configurado para registrar somente as mensagens associadas a uma ou mais IDs de evento. Por exemplo, para registrar somente mensagens para o contexto que está sendo inicializado ou descartado:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { CoreEventId.ContextDisposed, CoreEventId.ContextInitialized });

Categorias de mensagem

Cada mensagem de log é atribuída a uma categoria de agente hierárquico nomeado. As categorias são:

Categoria Mensagens
Microsoft.EntityFrameworkCore Todas as mensagens de EF Core
Microsoft. EntityFrameworkCore. Database Todas as interações de banco de dados
Microsoft. EntityFrameworkCore. Database. Connection Usos de uma conexão de banco de dados
Microsoft. EntityFrameworkCore. Database. Command Usos de um comando de banco de dados
Microsoft. EntityFrameworkCore. Database. Transaction Usos de uma transação de banco de dados
Microsoft. EntityFrameworkCore. Update Salvando entidades, excluindo interações de banco de dados
Microsoft. EntityFrameworkCore. Model Todas as interações de modelo e metadados
Microsoft. EntityFrameworkCore. Model. Validation Validação de modelo
Microsoft. EntityFrameworkCore. Query Consultas, excluindo interações de banco de dados
Microsoft. EntityFrameworkCore. Infrastructure Eventos gerais, como a criação de contexto
Microsoft. EntityFrameworkCore. scaffolding Engenharia reversa de banco de dados
Microsoft. EntityFrameworkCore. Migrations Migrações
Microsoft. EntityFrameworkCore. ChangeTracking Interações de controle de alterações

LogTo pode ser configurado para registrar somente as mensagens de uma ou mais categorias. Por exemplo, para registrar somente as interações de banco de dados:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name });

Observe que a DbLoggerCategory classe fornece uma API hierárquica para localizar uma categoria e evita a necessidade de embutir em código cadeias de caracteres.

Como as categorias são hierárquicas, este exemplo usando a Database categoria incluirá todas as mensagens para as subcategorias Database.Connection , Database.Command e Database.Transaction .

Filtros personalizados

LogTo permite que um filtro personalizado seja usado para casos em que nenhuma das opções de filtragem acima seja suficiente. Por exemplo, para registrar qualquer mensagem no nível Information ou acima, bem como mensagens para abrir e fechar uma conexão:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(
            Console.WriteLine,
            (eventId, logLevel) => logLevel >= LogLevel.Information
                                   || eventId == RelationalEventId.ConnectionOpened
                                   || eventId == RelationalEventId.ConnectionClosed);

Dica

A filtragem usando filtros personalizados ou o uso de qualquer uma das outras opções mostradas aqui é mais eficiente do que a filtragem no LogTo delegado. Isso ocorre porque, se o filtro determinar que a mensagem não deve ser registrada, a mensagem de log nem mesmo será criada.

Configuração de mensagens específicas

A API EF Core permite que os ConfigureWarnings aplicativos alterem o que acontece quando um evento específico é encontrado. Isso pode ser usado para:

  • Alterar o nível de log no qual o evento é registrado
  • Ignorar completamente o registro em log do evento
  • Lançar uma exceção quando o evento ocorrer

Alterando o nível de log de um evento

O exemplo anterior usou um filtro personalizado para registrar todas as mensagens em LogLevel.Information , bem como dois eventos definidos para LogLevel.Debug . O mesmo pode ser obtido alterando o nível de log dos dois Debug eventos para Information :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(
            b => b.Log(
                (RelationalEventId.ConnectionOpened, LogLevel.Information),
                (RelationalEventId.ConnectionClosed, LogLevel.Information)))
        .LogTo(Console.WriteLine, LogLevel.Information);

Suprimir o registro em log de um evento

De maneira semelhante, um evento individual pode ser suprimido do registro em log. Isso é particularmente útil para ignorar um aviso que foi revisado e compreendido. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Ignore(CoreEventId.DetachedLazyLoadingWarning))
        .LogTo(Console.WriteLine);

Lançar para um evento

Por fim, EF Core pode ser configurado para lançar para um determinado evento. Isso é particularmente útil para alterar um aviso em um erro. (Na verdade, essa era a finalidade original do ConfigureWarnings método, portanto, o nome.) Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
        .LogTo(Console.WriteLine);

Conteúdo e formatação da mensagem

O conteúdo padrão de LogTo é formatado em várias linhas. A primeira linha contém metadados de mensagem:

  • O LogLevel como um prefixo de quatro caracteres
  • Um timestamp local, formatado para a cultura atual
  • O no formulário que pode ser copiado/copiado para obter o membro de ou uma das outras classes, mais o valor da EventId CoreEventId EventId ID bruta
  • A categoria de evento, conforme descrito acima.

Por exemplo:

info: 10/6/2020 10:52:45.581 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 10/6/2020 10:52:45.582 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 10/6/2020 10:52:45.585 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

Esse conteúdo pode ser personalizado passando valores de DbContextLoggerOptions , conforme mostrado nas seções a seguir.

Dica

Considere usar Microsoft.Extensions.Logging para obter mais controle sobre a formatação de log.

Usando a hora UTC

Por padrão, os timestamps são projetados para consumo local durante a depuração. Use DbContextLoggerOptions.DefaultWithUtcTime para usar os timestamps UTC de cultura, mas mantenha todo o resto da mesma forma. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithUtcTime);

Este exemplo resulta na seguinte formatação de log:

info: 2020-10-06T17:55:39.0333701Z RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 2020-10-06T17:55:39.0333892Z RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 2020-10-06T17:55:39.0351684Z RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

Registro em log de linha única

Às vezes, é útil obter exatamente uma linha por mensagem de log. Isso pode ser habilitado pelo DbContextLoggerOptions.SingleLine . Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithLocalTime | DbContextLoggerOptions.SingleLine);

Este exemplo resulta na seguinte formatação de log:

info: 10/6/2020 10:52:45.723 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
dbug: 10/6/2020 10:52:45.723 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committing transaction.
dbug: 10/6/2020 10:52:45.725 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committed transaction.

Outras opções de conteúdo

Outros sinalizadores no DbContextLoggerOptions podem ser usados para cortar a quantidade de metadados incluídos no log. Isso pode ser útil em conjunto com o registro em log de linha única. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.UtcTime | DbContextLoggerOptions.SingleLine);

Este exemplo resulta na seguinte formatação de log:

2020-10-06T17:52:45.7320362Z -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
2020-10-06T17:52:45.7320531Z -> Committing transaction.
2020-10-06T17:52:45.7339441Z -> Committed transaction.

Movendo do EF6

EF Core registro em log simples difere Database.Log do EF6 de duas maneiras importantes:

  • As mensagens de log não estão limitadas apenas a interações de banco de dados
  • O log deve ser configurado no momento da inicialização do contexto

Para a primeira diferença, a filtragem descrita acima pode ser usada para limitar quais mensagens são registradas.

A segunda diferença é uma alteração intencional para melhorar o desempenho, não gerando mensagens de log quando elas não são necessárias. No entanto, ainda é possível obter um comportamento semelhante ao EF6 criando uma propriedade em seu e, em seguida, usando-a somente Log quando ela tiver sido DbContext definida. Por exemplo:

public Action<string> Log { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(s => Log?.Invoke(s));