로거가 클래스별로 로거 사용을 권장하는 이유는 무엇입니까?


88

NLog의 문서에 따르면 :

대부분의 응용 프로그램은 클래스 당 하나의 로거를 사용합니다. 여기서 로거 이름은 클래스 이름과 같습니다.

이것은 log4net이 작동하는 것과 같은 방식입니다. 이것이 좋은 관행 인 이유는 무엇입니까?


1
흠. 여기에 두 가지 문제가있는 것 같습니다. 하나는 클래스 당 실제 로그 객체를 갖고 있고, 하나는 로그 이름이 클래스와 동일하다는 것입니다.
Peter Recore 2010-06-29

답변:


59

log4net을 사용하면 클래스 당 하나의 로거를 사용하면 로그 메시지의 소스 (즉, 로그에 쓰는 클래스)를 쉽게 캡처 할 수 있습니다. 클래스 당 하나의 로거가없고 대신 전체 앱에 대해 하나의 로거가있는 경우 로그 메시지가 어디에서 오는지 알기 위해 더 많은 리플렉션 트릭에 의존해야합니다.

다음을 비교하십시오.

수업 별 로그

using System.Reflection;
private static readonly ILog _logger = 
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);    

public void SomeMethod()
{
    _logger.DebugFormat("File not found: {0}", _filename);
}

앱당 하나의 로거 (또는 유사)

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller

-- or --

Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller

두 번째 예제를 사용하면 Logger는 스택 추적을 작성하여 누가 호출했는지 확인해야합니다. 그렇지 않으면 코드가 항상 호출자에게 전달되어야합니다. logger-per-class 스타일을 사용하면 여전히이 작업을 수행하지만 호출 당 한 번이 아니라 클래스 당 한 번 수행 할 수 있으며 심각한 성능 문제를 제거 할 수 있습니다.


고마워요. 우리는 메시지에 클래스 이름과 메서드를 수동으로 넣었지만 (예 : "ImageCreator.CreateThumbnail () called"), 로거가 처리 할 수 ​​있으면 더 좋습니다.
Daniel T.

1
참고로, 스레드 정보와 같은 정보를 더 쉽게 캡처 할 수 있도록하기 때문에 클래스 당 (즉, 정적)이 아닌 인스턴스 당 Logger를 사용하는 것이 "더 나은"관행이됩니다. 분명히 그것은 맛의 문제이고 "단단하고 빠른 규칙"이 아닙니다.하지만 저는 그것을 버리고 싶었습니다.
Will Hartung

7
@will, 좀 더 설명해 주시겠습니까? Logger per Class를 사용하여 로깅 할 때 항상 스레드 ID를 로깅하므로 로거가 현재 스레드 정보를 얻을 수 있습니다. 다른 모든 스레드 정보도 로거에서 사용할 수 있습니다.
Jeremy Wiebe

@Jeremy Wiebe : 이것이 유일한 이유입니까? 전체 앱에 대해 로거 유형의 단일 전역 변수를 사용하면 기능적으로 문제가 없습니까?
giorgim

1
@Giorgi 아니요, 그렇게 생각하지 않습니다. 요즘에는 클래스 당 하나의 로거를 덜 관련성있게 만드는 CallerInformation 속성 ( msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe)

15

NLog에서 "파일 당 로거"사용의 장점 : 네임 스페이스 및 클래스 이름별로 로그를 관리 / 필터링 할 수 있습니다. 예:

<logger name="A.NameSpace.MyClass"      minlevel="Debug" writeTo="ImportantLogs" /> 
<logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" /> 
<logger name="StupidLibrary.*"          minlevel="Error" writeTo="StupidLibraryLogs" />

<!-- Hide other messages from StupidLibrary -->
<logger name="StupidLibrary.*" final="true" /> 

<!-- Log all but hidden messages -->
<logger name="*" writeTo="AllLogs" /> 

NLogger에는이를 수행하는 데 매우 유용한 코드가 있습니다. nlogger조각은 다음 코드를 생성합니다 :

private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

따라서 몇 번의 키 입력만으로 클래스 당 로거가 있습니다. 로거의 이름으로 네임 스페이스와 클래스 이름을 사용합니다. 클래스 로거에 다른 이름을 설정하려면 다음을 사용할 수 있습니다.

private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");

그리고 @JeremyWiebe가 말했듯이 메시지를 기록하려는 클래스의 이름을 얻기 위해 속임수를 사용할 필요가 없습니다. 로거의 이름 (일반적으로 클래스의 이름)은 파일에 쉽게 기록 될 수 있습니다. (또는 다른 대상) ${logger}레이아웃에서 사용 합니다.


5

이 선택에 대한 몇 가지 이유를 알 수 있습니다.

  • 로그 출력 형식에 로거 이름을 포함하면 특정 로그 문이 어디에서 왔는지 항상 알 수 있습니다.
  • 특정 로거를 켜거나 끄거나 레벨을 설정하여 세분화 된 레벨에서 표시되는 로그 문을 제어 할 수 있습니다.

4

NLog의 경우에도 성능상의 이점이 있습니다. 대부분의 사용자는

Logger logger = LogManager.GetCurrentClassLogger()

스택 추적에서 현재 클래스를 찾으려면 약간의 성능이 필요합니다.


3

대부분의 경우 클래스 이름은 로거에 좋은 이름을 제공합니다. 로그 파일을 스캔 할 때 로그 메시지를보고 코드 줄과 직접 연결할 수 있습니다.

이것이 최선의 방법이 아닌 좋은 예는 Hibernate의 SQL 로그입니다. "Hibernate.SQL"또는 이와 유사한 이름의 공유 로거가 있습니다. 여기서 여러 클래스가 원시 SQL을 단일 로거 범주에 기록합니다.


2

개발 관점에서 매번 로거 객체를 만들 필요가없는 것이 가장 쉽습니다. 반면에 그렇지 않고 리플렉션을 사용하여 동적으로 생성하면 성능이 저하됩니다. 이를 해결하려면 로거를 동적으로 비동기식으로 생성하는 다음 코드를 사용할 수 있습니다.

using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WinForms
{
    class log
    {

        public static async void Log(int severity, string message)
        {
            await Task.Run(() => LogIt(severity, message));
        }

        private static void LogIt(int severity, string message)
        {
            StackTrace st = new StackTrace();
            StackFrame x = st.GetFrame(2);     //the third one goes back to the original caller
            Type t = x.GetMethod().DeclaringType;
            Logger theLogger = LogManager.GetLogger(t.FullName);

            //https://github.com/NLog/NLog/wiki/Log-levels
            string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" };
            int level = Math.Min(levels.Length, severity);
            theLogger.Log(LogLevel.FromOrdinal(level), message);

        }
    }
}

1

두 가지 이유가 즉시 떠 오릅니다.

  1. 각 클래스에 대해 별도의 로그가 있으면 특정 클래스와 관련된 모든 로그 메시지 / 오류를 쉽게 그룹화 할 수 있습니다.
  2. 클래스 내에 로그가 있으면 클래스 외부에서 액세스 할 수없는 내부 세부 정보 (예 : 개인 상태, 클래스 구현을 다루는 정보 등)를 로깅 할 수 있습니다.

2
클래스 수준에서 또는 전역 적으로 정의되었는지 여부에 관계없이 클래스에 로거가 있습니다. 전역 로거는 가시성 관점에서 볼 때 클래스 "외부"가 아닙니다. 여전히 문제의 클래스 에서 전역 로거를 참조하고 있으므로 완전한 가시성을 갖습니다.
로버트

0

아마도 캡슐화를 중단하지 않고 클래스에만 표시되는 메서드를 로깅 할 수 있기를 원하기 때문에 로깅 기능을 중단하지 않고 다른 애플리케이션에서 클래스를 쉽게 사용할 수 있습니다.


1
다른 응용 프로그램에서 해당 클래스를 사용하기 어렵게 만듭니다. 원하든 원하지 않든 로깅 라이브러리를 참조해야합니다.
Piotr Perak 2011

0

네임 스페이스 또는 클래스별로 어 펜더를 쉽게 구성 할 수 있습니다.


0

NLOG를 사용하는 경우 구성에서 호출 사이트를 지정할 수 있습니다. 그러면 로깅 문이있는 클래스 이름과 메서드가 기록됩니다.

<property name="CallSite" value="${callsite}" />

그런 다음 로거 이름 또는 어셈블리 이름에 상수를 사용할 수 있습니다.

면책 조항 : NLOG가이 정보를 수집하는 방법을 모르겠습니다. 제 추측은 반영 일 것이므로 성능을 고려해야 할 수도 있습니다. NLOG v4.4 이상을 사용하지 않는 경우 Async 메서드에 몇 가지 문제가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.