IFoxLog Interface¶
Fox Logging이 제공하는 로깅 기능의 핵심은 IFoxLog
인터페이스를 통해 제공됩니다. 어플리케이션은 IFoxLog
인터페이스를 통해 로그를 기록하기만 하면 구성 설정을 통해 실제 로그가 기록될 매체(콘솔, 디버거, 파일, 데이터베이스 등)를 결정할 수 있기 때문 입니다.
IFoxLog Interface Overview¶
IFoxLog
인터페이스는 다음과 같이 정의되어 있습니다. IFoxLog
인터페이스의 속성 및 메서드들은 이 문서내에서 모두 언급되어 설명할 것입니다.
어플리케이션은 FoxLogManager
클래스의 GetLogger
메서드를 호출하여 등록된 로거들 중 하나에 대한 IFoxLog
인터페이스를 구할 수 있습니다.
IFoxLog log = FoxLogManager.GetLogger("MyLogger");
log.Write(FoxLogLevel.Information, "This is a log entry.");
Write method¶
로그를 기록하기 위해 IFoxLog
인터페이스는 Write
메서드와 WriteFormat
메서드를 제공합니다. Write
메서드는 로그 수준과 로그 데이터를 사용하여 로그를 기록할 수 있으며 WriteFormat
메서드는 로그 메시지를 포맷팅하여 로그를 기록합니다. WriteFormat
메서드는 로그가 필터링될 때 불필요한 포맷팅을 수행하지 않도록 제공되는 메서드 입니다.
로그 데이터가 어떻게 로그 매체에 기록될 것인가는 로거에 따라 다릅니다. FoxConsoleLogger
, FoxTextFileLogger
클래스가 구현하는 로거는 로그 데이터를 단순히 문자열로 변환(ToString
메서드)하여 로그에 기록합니다. 예를 들어, 예외가 발생하면 Exception
객체를 로그로 기록하는 다음 코드를 생각해 봅시다.
로거가 FoxConsoleLogger
나 FoxTextFileLogger
라면 Exception.ToString()
메서드가 호출되어 문자열이 로그에 기록될 것입니다. 하지만 모든 로거가 문자열에 의존하지는 않습니다. 커스텀 로거를 작성하여 Exception.Message
속성만 기록하는 로거를 작성할 수도 있습니다.
WriteFormat method¶
로그를 기록할 때, 많은 경우 문자열 포맷팅을 사용합니다. 예를 들어, 메서드에 전달된 매개변수를 로그로 남긴다면 다음과 같은 코드를 사용할 수 있습니다.
위와 같은 코드는 가독성이 좋지만 효율성 면에서 매우 좋지 않습니다. C# 문자열 보간(interpolation)이 사용되어 항상 포맷팅을 수행하고 새로운 문자열 객체를 할당하기 때문입니다. 만약 로거의 로그 필터링에 의해 Information
로그 수준이 필터링되는 경우 문자열 포맷팅 및 할당은 전혀 불필요한 작업이 되기 때문입니다. 일반적으로 개발 시 대량의 로그를 매우 상세하게 기록하기 때문에 불필요한 포맷팅과 문자열 할당은 성능적인 문제를 유발할 수도 있습니다.
WriteFormat
메서드는 이러한 문제를 해결하기 위해 제공되는 메서드입니다. WriteFormat
메서드는 C# 문자열 보간 스타일의 포맷팅이 아닌 구식 String.Format
스타일의 포맷팅을 사용하긴 하지만 로그가 필터링되지 않는 경우에만 포맷팅을 수행합니다. 따라서 위 코드는 다음과 같이 WriteFormat
메서드를 사용하여 효율적으로 바꿀 수 있습니다.
WriteFormat
메서드를 사용하면 로그가 필터링 되는 경우 불필요한 포맷팅과 문자열 할당을 피할 수 있습니다. 로거가 정의되어 있지 않아 대체 로거가 사용되는 경우에도 대체 로거는 문자열 포맷팅과 할당을 수행하지 않기 때문에 성능 저하를 유발하지 않습니다.
Logger Name¶
Fox Logging에서 로거의 이름은 매우 중요합니다. 어플리케이션은 사용하고자 하는 로거의 IFoxLog
인터페이스를 구하기 위해 로거 이름을 사용하여 GetLogger
메서드를 호출하며 로깅 구성 설정에서 로거를 등록할 때에도 로거를 구분하는 유일한 방법으로 로거 이름을 사용하기 때문입니다. 로거 이름은 IFoxLog.Name
속성을 통해 접근이 가능합니다.
로거 이름은 로거가 생성될 때 설정되며 이후 변경할 수 없습니다. 로거는 구성 설정을 통해 생성되고 등록되며 동일한 이름의 로거는 등록될 수 없습니다.
FoxLogManager.RegisterLogger
메서드를 통해 코드를 사용하여 로거를 등록할 수도 있습니다. 이 경우에도 동일한 이름의 로거를 등록하고자 하는 시도는 예외를 발생하게 됩니다.
어플리케이션이 GetLogger
메서드를 호출할 때 사용하는 로거 이름과 반환된 IFoxLog
인터페이스의 Name
속성이 동일하지 않을 수 있습니다. 이는 Fox Logging이 제공하는 대체 로거(fallback logger)와 로거 이름 레벨링 기능 때문입니다.
IFoxLog log = FoxLogManager.GetLogger("NonExistLoggerName");
Console.WriteLine($"log.Name = {log1.Name}");
위 코드는 등록되지 지은 로거 이름을 사용하여 GetLogger
를 호출한 경우 입니다. 이 경우, FoxLogManager
클래스는 대체 로거를 반환합니다. 대체 로거의 이름은 NeoDEEX.FallbackLogger
로 고정되어 있습니다.
대체 로거와 로거 이름 레벨링에 대해서는 관련 문서를 참고 하십시요.
Log Source¶
로그 소스는 로그를 남기는 주체를 나타냅니다. 로그 소스는 FoxConsoleLogger
, FoxDebugLogger
, FoxTextFileLogger
와 같이 로그 메시지(문자열)를 남기는 로거의 경우 []
문자로 로그 소스를 표시합니다. 다음 로그 예제에서 로그 소스는 MyLogger
입니다.
I 2022-04-07 08:49:51.91432 [MyLogger] This is a log entry.
I 2022-04-07 08:49:51.91939 [MyLogger] Method parameter: id=TestUser arg=some data
로그 소스는 Write
와 WriteFormat
메서드의 매개변수로 지정할 수 있습니다. 다음 에제 코드에서 첫번째 매개변수가 로그 소스 이름을 나타냅니다.
로그 소스를 명시하지 않는 Write
, WriteFormat
메서드가 호출되면 로거의 디폴트 소스 이름이 사용됩니다. 디폴트 소스 이름은 로거가 생성될 때 로거 이름으로 초기화 됩니다. 예를 들어, 다음 호출에서 소스 이름은 MyLogger
가 됩니다.
IFoxLog log = FoxLogManager.GetLogger("MyLogger");
log.Write(FoxLogLevel.Information, "Wihtout source parameter.");
로거의 디폴트 소스 이름은 IFoxLog.Source
속성을 통해 접근이 가능합니다. 이 속성을 사용하여 디폴트 소스 이름을 변경할 수 있습니다. 이 때 주의할 사항은 이 변경은 이후 이 로거를 사용하는 모든 로그 기록에 영향을 준다는 것입니다. 특히, 여러 스레드가 동시에 하나의 로거를 통해 로그를 기록하는 경우 의도하지 않은 소스 이름이 사용될 수도 있습니다.
디폴트 로그 소스에 의존하여 로그 소스를 관리하는 것은 여러가지 문제를 유발할 수도 있습니다. 로그 소스를 명확히 로그에 남겨야 한다면 권장되는 방법은 Write
혹은 WriteFormat
메서드 호출 시 로그 소스를 명시하는 것입니다. 다음과 같은 헬퍼 메서드를 작성하여 로그를 기록하는 주체(여기에선 클래스 이름)를 로그 소스로 명시하면 됩니다.
Log Level & Filter¶
대부분의 로깅 프레임워크와 마찬가지로 Fox Logging 역시 로그의 수준을 정의합니다. Fox Logging에서 사용하는 로그 수준은 다음과 같습니다.
Critical
심각한 수준의 문제를 나타내는 로그 수준입니다. 가장 높은 수준인 만큼 필터링을 통해 필터링되지 않습니다.
Error
예외(exception)과 같은 오류를 나타내는 로그 수준입니다.
Warning
오류는 아니지만 주의가 필요함을 나타내는 로그 수준입니다. Warning
로그 수준은 Fox Logging의 디폴트 필터 수준입니다.
Information
참고 사항 정도의 정보를 나타내는 로그 수준입니다.
Verbose
디버깅 혹은 추적(trace)용으로 사용되는 로그 수준입니다.
이들 로그 수준은 FoxLogLevel
열거 타입(enum type)으로 정의됩니다. 가장 높은 수준은 Critical
이며 가장 낮은 수준은 Verbose
입니다. 어플리케이션이 로그를 남길 때에는 항상 해당 로그가 어떤 로그 수준인지를 명시해야 합니다. 실제로 Write
메서드 및 WriteFormat
메서드는 매개변수로 로그 수준을 요구합니다.
로거는 로그 수준에 따라 로그를 기록하지 않도록 하는 필터링 기능을 제공합니다. 로거의 필터링은 IFoxLog.FilterLevel
속성을 통해 접근이 가능합니다. 기본 필터값은 Warning
이 사용됩니다.
Warning
IFoxLog.FilterLevel
속성 값을 변경하면 해당 로거를 사용하는 모든 코드들이 영향을 받습니다. 특히, 여러 스레드가 동시에 단일 로거를 사용하여 로그를 기록하는 상황에서 한 스레드가 필터값을 수정하면 다른 스레드들이 즉시 영향을 받게 됩니다.
구성 설정을 통해 로거가 생성될 때 로거의 필터 수준을 지정할 수 있습니다. 다음은 MyLogger
의 필터 수준을 Information
으로 지정하는 구성 설정입니다.
구성 설정에서 로거의 필터가 지정되지 않았다면 전역 필터 수준이 사용됩니다. 전역 필터 수준은 FoxLogManager.GlobalFilterLevel
속성을 통해 접근할 수 있습니다. 전역 필터 수준은 FoxLogManager.GlobalFilterLevel
속성을 직접 수정하거나 구성 설정을 통해 지정할 수 있습니다.
다음 구성 설정은 "logging:filter"
속성을 통해 전역 필터 수준을 Verbose
로 설정하고 있으며 SomeLogger
는 명시적으로 필터를 지정하지 않음으로서 전역 필터 수준인 Verbose
를 사용합니다.
필터값 보다 낮은 수준의 로그 기록은 필터링 되어 로그에 기록되지 않습니다. 예를 들어 필터가 Warning
으로 지정되면 Warning
이하의 로그 수준(Information
및 Verbose
)은 로그에 기록되지 않습니다. 특정 로그 수준이 필터링 되는지 여부를 확인하난 방법으로 IFoxLog
인터페이스는 IsEnabled
메서드를 제공합니다. IsEnabled
메서드를 사용하여 현재 로거가 주어진 로그 수준을 필터링하는지 여부를 파악할 수 있습니다. 다음 코드는 로거가 어떤 로그 수준을 필터링하는지를 표시하는 예제 코드 입니다.
Note
대체 로거로 사용되는 FoxDummyLogger
는 필터 설정에 무관하게 모든 로그 메시지를 필터링 합니다.
IFoxLog extension method¶
NeoDEEX.Diagnostics
네임스페이스의 FoxLogExtensions
정적 클래스는 IFoxLog
인터페이스를 위한 확장 메서드들을 제공합니다. 이들 확장 메서드들은 로깅 시 FoxLogLevel
매개변수를 생략할 수 있도록 해줍니다. 예를 들어 다음과 같은 코드가 있을 때,
위 코드는 확장 메서드들을 이용하여 다음과 같이 작성할 수 있습니다.
확장 메서드를 사용하면 좀 더 간단하고 가독성 높은 코드를 작성할 수 있습니다. 하지만 확장 메서드들은 로그 소스를 명시할 수 없습니다. 이는 메서드 오버로드에서 모호성을 유발할 수 있기 때문입니다. 따라서 로그 소스를 명시하여 로그를 기록하고자 한다면 여전히 Write
혹은 WriteFormat
메서드를 사용해야 합니다.
예제 코드¶
이 문서에서 다루었던 예제 코드들은 Simple Loggging 예제 코드 에서 찾을 수 있습니다.