간단한 로깅
팁
GitHub에서 이 문서의 샘플을 다운로드할 수 있습니다.
EF Core(Entity Framework Core) 간단한 로깅을 사용하여 애플리케이션을 개발하고 디버깅하는 동안 로그를 쉽게 가져올 수 있습니다. 이 형태의 로깅에는 최소한의 구성과 추가 NuGet 패키지가 필요하지 않습니다.
팁
EF Core는 더 많은 구성이 필요하지만 프로덕션 애플리케이션에서 로깅하는 데 더 적합한 Microsoft.Extensions.Logging과도 통합됩니다.
구성
EF Core 로그는 DbContext 인스턴스를 구성할 때 LogTo를 사용하여 어떤 유형의 애플리케이션에서도 액세스할 수 있습니다. 이 구성은 일반적으로 DbContext.OnConfiguring의 재정의에서 수행됩니다. 예시:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);
LogTo
는 AddDbContext 또는 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는 예외 메시지에 데이터의 값을 포함하지 않습니다. 이러한 데이터는 기밀일 수 있으며 예외가 처리되지 않으면 프로덕션 용도로 표시될 수 있기 때문입니다.
그러나 특히 키에 대한 데이터 값을 아는 것은 디버깅할 때 매우 유용할 수 있습니다. EnableSensitiveDataLogging()을 호출하여 EF Core에서 사용하도록 설정할 수 있습니다. 예시:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine)
.EnableSensitiveDataLogging();
자세한 쿼리 예외
성능상의 이유로 EF Core는 try-catch 블록에서 데이터베이스 공급자의 값을 읽기 위해 각 호출을 래핑하지 않습니다. 그러나 이로 인해 특히 모델에서 허용하지 않을 때 데이터베이스가 NULL을 반환하는 경우 진단하기 어려운 예외가 발생하는 경우가 있습니다.
EnableDetailedErrors를 켜면 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가 있을 수 있습니다. 예를 들어 SQL Server 공급자의 경우 SqlServerEventId입니다.
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를 사용하면 애플리케이션이 특정 이벤트가 발생할 때 발생하는 작업을 변경할 수 있습니다. 이를 사용하여 다음을 수행할 수 있습니다.
- 이벤트가 기록되는 로그 수준 변경
- 이벤트 로깅 모두 건너뛰기
- 이벤트가 발생할 때 예외를 throw합니다.
이벤트의 로그 수준 변경
이전 예제에서는 사용자 지정 필터를 사용하여 LogLevel.Debug
에 대해 정의된 두 개의 이벤트뿐만 아니라 LogLevel.Information
의 모든 메시지를 기록했습니다. 두 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);
이벤트에 대한 throw
마지막으로, 지정된 이벤트에 대해 EF Core를 throw하도록 구성할 수 있습니다. 이 기능은 경고를 오류로 변경하는 데 특히 유용합니다. (실제로 이는 ConfigureWarnings
메서드의 원래 목적이므로 그러한 이름이 붙여졌습니다.) 예를 들어:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
.LogTo(Console.WriteLine);
메시지 내용 및 서식 지정
LogTo
의 기본 콘텐츠는 여러 줄에 걸쳐 서식이 지정됩니다. 첫 번째 줄에는 메시지 메타데이터가 포함됩니다.
- 4자 접두사인 LogLevel
- 현재 문화권에 대한 형식이 지정된 로컬 타임스탬프
- CoreEventId 또는 다른
EventId
클래스 중 하나에서 멤버를 가져오기 위해 복사/붙여넣을 수 있는 양식의 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에서 Database.Log와 다릅니다.
- 로그 메시지는 데이터베이스 상호 작용으로만 제한되지 않습니다.
- 컨텍스트 초기화 시 로깅을 구성해야 합니다.
첫 번째 차이점의 경우 위에서 설명한 필터링을 사용하여 기록되는 메시지를 제한할 수 있습니다.
두 번째 차이점은 필요하지 않을 때 로그 메시지를 생성하지 않음으로써 성능을 개선하기 위한 의도적인 변경입니다. 그러나 DbContext
에서 Log
속성을 만든 다음 설정된 경우에만 사용하여 EF6과 유사한 동작을 가져올 수 있습니다. 예시:
public Action<string> Log { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(s => Log?.Invoke(s));
.NET