팁 (조언)
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);
대안적으로, 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는 예외 메시지에 데이터의 값을 포함하지 않습니다. 이러한 데이터는 기밀일 수 있으며 예외가 처리되지 않는 경우 프로덕션 사용에서 공개될 수 있기 때문입니다.
그러나 특히 키에 대한 데이터 값을 아는 것은 디버깅할 때 매우 유용할 수 있습니다. 이 기능은 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));
.NET