다음을 통해 공유


단순 로깅

팁 (조언)

GitHub에서 이 문서의 샘플을 다운로드할 수 있습니다.

EF Core(Entity Framework Core) 간단한 로깅을 사용하여 애플리케이션을 개발하고 디버깅하는 동안 로그를 쉽게 가져올 수 있습니다. 이 형태의 로깅에는 최소한의 구성과 추가 NuGet 패키지가 필요하지 않습니다.

팁 (조언)

또한 EF Core는 Microsoft.Extensions.Logging과 통합되므로 더 많은 구성이 필요하지만 프로덕션 애플리케이션에서 로깅하는 데 더 적합한 경우가 많습니다.

구성 / 설정

EF Core 로그는 LogTo을(를) 사용하여 모든 유형의 애플리케이션에서 액세스할 수 있습니다. 이 구성은 일반적으로 DbContext.OnConfiguring의 재정의에서 이루어집니다. 다음은 그 예입니다.

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

대안적으로, LogToAddDbContext의 일부로 호출하거나 DbContextOptions 인스턴스를 생성하여 DbContext 생성자에 전달할 때 호출할 수 있습니다.

팁 (조언)

AddDbContext를 사용하거나 DbContextOptions 인스턴스가 DbContext 생성자에 전달될 때 OnConfiguring은 여전히 호출됩니다. 따라서 DbContext가 생성되는 방식에 관계없이 컨텍스트 구성을 적용하는 것이 좋습니다.

로그 관리

콘솔에 로깅

LogTo 에는 문자열을 Action<T> 허용하는 대리자가 필요합니다. EF Core는 생성된 각 로그 메시지에 대한 문자열을 사용하여 이 대리자를 호출합니다. 그러면 대리자가 지정된 메시지로 작업을 수행할 수 있습니다.

Console.WriteLine 메서드는 위에서 설명한 대로 이 델리게이트에 자주 사용됩니다. 그러면 각 로그 메시지가 콘솔에 기록됩니다.

디버그 창에 로깅

Debug.WriteLine 는 Visual Studio 또는 다른 IDE의 디버그 창으로 출력을 보내는 데 사용할 수 있습니다. 이 경우 클래스가 릴리스 빌드에서 컴파일되므로 Debug 사용해야 합니다. 다음은 그 예입니다.

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

파일에 로깅

파일에 쓰려면 파일에 대한 StreamWriter 또는 유사한 객체를 만들어야 합니다. 그런 다음 위의 WriteLine 다른 예제와 같이 메서드를 사용할 수 있습니다. 컨텍스트가 해제될 때 작성기를 해제하여 파일이 깔끔하게 닫히도록 해야 합니다. 다음은 그 예입니다.

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();
}

팁 (조언)

프로덕션 애플리케이션의 파일에 로깅하기 위해 Microsoft.Extensions.Logging 을 사용하는 것이 좋습니다.

자세한 메시지 가져오기

민감 데이터

기본적으로 EF Core는 예외 메시지에 데이터의 값을 포함하지 않습니다. 이러한 데이터는 기밀일 수 있으며 예외가 처리되지 않는 경우 프로덕션 사용에서 공개될 수 있기 때문입니다.

그러나 특히 키에 대한 데이터 값을 아는 것은 디버깅할 때 매우 유용할 수 있습니다. 이 기능은 EF Core에서 호출 EnableSensitiveDataLogging()하여 사용하도록 설정할 수 있습니다. 다음은 그 예입니다.

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

자세한 쿼리 예외

성능상의 이유로 EF Core는 try-catch 블록에서 데이터베이스 공급자의 값을 읽기 위해 각 호출을 래핑하지 않습니다. 그러나 이로 인해 진단하기 어려운 예외가 발생하는 경우가 있습니다. 특히 모델에서 허용되지 않는 경우 데이터베이스가 NULL을 반환하는 경우에 특히 그렇습니다.

EF 기능을 활성화하면 이러한 try-catch 블록이 도입되어 더 상세한 오류 메시지가 제공됩니다. 다음은 그 예입니다.

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

필터링

로그 수준

모든 EF Core 로그 메시지는 열거형에 의해 LogLevel 정의된 수준에 할당됩니다. 기본적으로 EF Core 단순 로깅은 Debug 수준 이상의 모든 메시지를 포함합니다. LogTo 는 일부 메시지를 필터링하기 위해 더 높은 최소 수준을 전달할 수 있습니다. 예를 들어 결과를 전달 Information 하면 데이터베이스 액세스 및 일부 하우스키핑 메시지로 제한되는 최소 로그 집합이 생성됩니다.

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

특정 메시지

모든 로그 메시지에는 .가 EventId할당됩니다. 이러한 ID는 관계형 관련 메시지를 위해 CoreEventId 클래스 또는 RelationalEventId 클래스에서 액세스할 수 있습니다. 데이터베이스 공급자도 비슷한 클래스에 공급자별 ID가 있을 수 있습니다. 예를 들어 SqlServerEventId SQL Server 공급자의 경우입니다.

LogTo 은 하나 이상의 이벤트 ID와 연결된 메시지만 기록하도록 구성할 수 있습니다. 예를 들어 초기화되거나 삭제되는 컨텍스트에 대한 메시지만 기록하려면 다음을 수행합니다.

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

메시지 범주

모든 로그 메시지는 명명된 계층적 로거 범주에 할당됩니다. 범주는 다음과 같습니다.

카테고리 메시지
Microsoft.EntityFrameworkCore 모든 EF Core 메시지
Microsoft.EntityFrameworkCore.Database 모든 데이터베이스 상호 작용
Microsoft.EntityFrameworkCore.Database.Connection 데이터베이스 연결 사용
Microsoft.EntityFrameworkCore.Database.Command 데이터베이스 명령 사용
Microsoft.EntityFrameworkCore.Database.Transaction 데이터베이스 트랜잭션 사용
Microsoft.EntityFrameworkCore.Update 데이터베이스 연동을 제외한 엔터티 저장
Microsoft.EntityFrameworkCore.Model 모든 모델 및 메타데이터 상호 작용
Microsoft.EntityFrameworkCore.Model.Validation 모델 유효성 검사
Microsoft.EntityFrameworkCore.Query 데이터베이스 상호 작용을 제외한 쿼리
Microsoft.EntityFrameworkCore.Infrastructure 컨텍스트 만들기와 같은 일반 이벤트
Microsoft.EntityFrameworkCore.Scaffolding 데이터베이스 리버스 엔지니어링
Microsoft.EntityFrameworkCore.Migrations 마이그레이션
Microsoft.EntityFrameworkCore.ChangeTracking 변경 추적 상호 작용

LogTo 은 하나 이상의 범주에서만 메시지를 기록하도록 구성할 수 있습니다. 예를 들어 데이터베이스 상호 작용만 기록하려면 다음을 수행합니다.

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

클래스는 DbLoggerCategory 범주를 찾기 위한 계층적 API를 제공하고 문자열을 하드 코딩할 필요가 없습니다.

범주는 계층 구조이므로, 이 예제에서 Database 범주를 사용할 경우 하위 범주 Database.Connection, Database.Command, Database.Transaction에 대한 모든 메시지가 포함됩니다.

사용자 지정 필터

LogTo 를 사용하면 위의 필터링 옵션 중 어느 것도 충분하지 않은 경우 사용자 지정 필터를 사용할 수 있습니다. 예를 들어 수준 Information 이상의 메시지와 연결을 열고 닫기 위한 메시지를 기록하려면 다음을 수행합니다.

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

팁 (조언)

사용자 지정 필터를 사용하거나 여기에 표시된 다른 옵션을 사용하여 필터링하는 LogTo 것이 대리자의 필터링보다 더 효율적입니다. 필터가 메시지를 기록해서는 안 된다고 판단하면 로그 메시지도 생성되지 않기 때문입니다.

특정 메시지에 대한 구성

EF Core ConfigureWarnings API를 사용하면 애플리케이션이 특정 이벤트가 발생할 때 발생하는 작업을 변경할 수 있습니다. 이를 사용하여 다음을 수행할 수 있습니다.

  • 이벤트가 기록되는 로그 수준 변경
  • 이벤트 로깅 모두 건너뛰기
  • 이벤트가 발생할 때 예외를 발생시키십시오

이벤트의 로그 수준 변경

이전 예제에서는 사용자 지정 필터를 사용하여 모든 메시지 LogLevel.Information 와 정의된 두 개의 이벤트를 기록했습니다 LogLevel.Debug. 두 Debug 이벤트의 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);

이벤트 로깅 표시 안 함

마찬가지로 개별 이벤트를 로깅에서 표시하지 않을 수 있습니다. 이는 검토 및 이해된 경고를 무시하는 데 특히 유용합니다. 다음은 그 예입니다.

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

이벤트를 주최하다

마지막으로, 지정된 이벤트에 대해 EF Core를 throw하도록 구성할 수 있습니다. 이는 경고를 오류로 변경하는 데 특히 유용합니다. (원래 이 방법의 목적이었기 때문에 이런 이름이 붙었습니다.) 예를 들어:

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

메시지 내용 및 서식 지정

기본 콘텐츠 LogTo 는 여러 줄에 걸쳐 서식이 지정됩니다. 첫 번째 줄에는 메시지 메타데이터가 포함됩니다.

  • LogLevel는 4자로 된 접두사로 사용됩니다.
  • 현재 문화권에 대한 형식이 지정된 로컬 타임스탬프
  • EventId 복사/붙여넣기 가능한 형태로 CoreEventId 또는 다른 EventId 클래스 중 하나에서 멤버를 가져오는 것, 그리고 원시 ID 값을 제공합니다.
  • 위에서 설명한 대로 이벤트 범주입니다.

다음은 그 예입니다.

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.

이 콘텐츠는 다음 섹션과 같이 값을 DbContextLoggerOptions전달하여 사용자 지정할 수 있습니다.

팁 (조언)

로그 서식을 더 자세히 제어하려면 Microsoft.Extensions.Logging 을 사용하는 것이 좋습니다.

UTC 시간 사용

기본적으로 타임스탬프는 디버깅하는 동안 로컬 사용을 위해 설계되었습니다. 문화권에 구애받지 않은 UTC 타임스탬프를 대신 사용 DbContextLoggerOptions.DefaultWithUtcTime 하지만 다른 모든 타임스탬프는 동일하게 유지합니다. 다음은 그 예입니다.

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

이 예제에서는 다음과 같은 로그 서식을 지정합니다.

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.

한 줄 기록

로그 메시지당 정확히 한 줄을 가져오는 것이 유용한 경우도 있습니다. 이는 DbContextLoggerOptions.SingleLine로 활성화할 수 있습니다. 다음은 그 예입니다.

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

이 예제에서는 다음과 같은 로그 서식을 지정합니다.

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.

기타 콘텐츠 옵션

다른 플래그를 사용하여 로그에 DbContextLoggerOptions 포함된 메타데이터의 양을 트리밍할 수 있습니다. 이는 단일 줄 로깅과 함께 유용할 수 있습니다. 다음은 그 예입니다.

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

이 예제에서는 다음과 같은 로그 서식을 지정합니다.

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.

EF6에서 이동

EF Core의 단순 로깅은 두 가지 중요한 측면에서 EF6와 다릅니다.

  • 로그 메시지는 데이터베이스 상호 작용으로만 제한되지 않습니다.
  • 로깅은 컨텍스트 초기화 시 구성해야 합니다.

첫 번째 차이점의 경우 위에서 설명한 필터링을 사용하여 기록되는 메시지를 제한할 수 있습니다.

두 번째 차이점은 필요하지 않은 로그 메시지를 생성하지 않음으로써 성능을 개선하기 위한 의도적인 변경입니다. 그러나 EF6과 유사한 동작을 얻기 위해 Log에 속성을 생성하고, DbContext 설정된 경우에만 사용할 수 있습니다. 다음은 그 예입니다.

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

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