디자인 관점에서 로깅에 대한 모범 사례는 무엇입니까? [닫은]


11

현재 작업중 인 응용 프로그램에 로깅을 추가하고 싶습니다. 전에 로깅을 추가했지만 여기서는 문제가되지 않습니다.

그러나 객체 지향 언어의 디자인 관점에서 OOP 및 패턴을 따르는 로깅에 대한 모범 사례는 무엇입니까?

참고 : 현재 C # 에서이 작업을 수행하고 있으므로 C #의 예제는 분명히 환영합니다. Java와 Ruby로 예제를보고 싶습니다.


편집 : log4net을 사용하고 있습니다. 나는 그것을 연결하는 가장 좋은 방법이 무엇인지 모른다.

답변:


6

권장하는 가장 좋은 방법은 자신의 롤링 대신 log4j 를 사용 하는 것입니다. (Java에서 C #과 Ruby로 모두 포팅되었으므로 관심있는 3 개 언어 모두에 적용됩니다.)

해당 매뉴얼 페이지를 읽으면 몇 가지 다른 모범 사례를 발견 할 수 있습니다. 가볍고 응용 프로그램 외부에서 구성 할 수있는 것과 같이 응용 프로그램의 다른 부분에 대한 로깅을 독립적으로 설정 및 해제 할 수 있습니다.


5

내가 일하는 곳에서 많은 .NET 데스크톱 앱을 작성합니다. 우리는 일반적으로 컴포넌트에 2 개의 이벤트를 구현합니다. 하나는 정보 로깅 용이고 다른 하나는 예외 로깅 용입니다 (단, 별도의 이벤트를 발생시키는 대신 예외가 발생하지만 상황에 따라 다릅니다). 이 아키텍처를 사용하면 어떠한 라이브러리도 로깅이 구현되는 방법이나 정보의 사용, 저장 또는 처리 방법을 알 필요가 없습니다. 그런 다음 응용 프로그램이 해당 응용 프로그램에 적합한 방식으로 로깅 이벤트를 처리하도록합니다. 몇 년 전이 아키텍처는 MS Enterprise Library 로깅 사용에서 BitFactory의 로깅 구성 요소로의 전환을 매우 간단한 전환으로 만들었습니다.


+1 이벤트 / 관찰자 패턴 사용 : 관찰자를 변경하고 로깅을 변경했습니다
Matthieu M.


2

개인적으로, 나는 선택한 로깅 프레임 워크 (내 경우에는 .NET으로 작업하기 때문에 Entlib)를 사용하고 로깅을위한 AOP 측면을 작성합니다.

그런 다음 모든 메소드 / 속성 / 클래스 / 네임 스페이스의 속성을 지정하고 소스를 어지럽히 지 않고 로깅을 추가 할 수 있습니다.


매우 흥미있는 것처럼 들리지만, 나는 당신이 무엇을 기록 할 수 있고, 어떻게 정보가 기록 될 것인지에 대한 예약을 가지고 있습니다. 수행 할 수있는 작업과 수행 할 수없는 작업을 확인하기 위해이 방법의 실제 예를보고 싶습니다. 특히 새로운 앱을 시작하면서 어디서 / 얼마나 멀리 갈 수 있는지 알고 싶습니다.
Marjan Venema

@marjan Venema : 예리한 문서에는 분석법의 입력 / 종료를 기록하는 측면의 예가 있습니다. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… Post sharp의 경우 빌드시 속성에서 소스로 코드를 짜서 다른 사람들처럼 성능에 영향을 미치지 않습니다.
Steven Evers

1

현재 작업중 인 시스템은 이벤트 중심 아키텍처 및 메시징을 사용하므로 시스템의 대부분의 작업은 명령의 결과이며 표준 위임 이벤트가 아닌 디스패치 된 DTO 클래스와 같은 이벤트가 발생합니다. 로깅을 처리하는 것이 유일한 목적인 이벤트 핸들러를 첨부합니다. 이 디자인은 우리 자신을 반복하지 않고 기능을 추가 / 변경하기 위해 기존 코드를 수정할 필요가 없습니다.

다음은 애플리케이션의 좁은 섹션 (가져 오는 하나의 특정 컨텐츠 소스에 관한 것)에서 로깅 될 모든 이벤트를 처리하는 하나의 로깅 클래스의 예입니다.

자주 로그하는 방법과 방법에 대한 내 생각을 바꾸는 것처럼 보이기 때문에 이것이 최선의 방법이라고 말할 필요는 없습니다. 문제를 진단하기 위해 로그를 사용해야 할 때마다 필연적으로 개선 방법을 찾습니다. 내가 기록한 정보.

그러나 적절한 정보를 기록하는 것이 (특히 Ctrl-F / 찾기 검색 가능) 가장 중요한 부분이라고 말할 것입니다.

두 번째 가장 중요한 부분은 메인 논리에서 멀리 로깅 코드를 받고있다 -이 방법은 추하고 긴 뒤얽힌 할 수 있습니다 매우 빠르게.

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

다른 사람이 말했듯이, 사용 log4j하거나 log4net또는 다른 잘 구축 로깅 프레임 워크.

나는 비즈니스 로직을 방해하는 로깅 코드를 정말로 싫어합니다. 내가 사용하는 이유 Log4PostSharp입니다. 즉, Aspect Oriented Programming 을 사용 하여 다음과 같은 메소드에 주석을 달 수 있습니다 .

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

또는 다음과 같은 어셈블리의 모든 메소드 :

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

어떤 프레임 워크 가이 작업을 수행하는지 확실하지 않지만 디자인 관점에서 정보를 주로 세 가지 범주로 로그인해야한다고 모델링합니다.

  1. 메소드 레벨 추적
  2. 예외 로깅
  3. 추가 런타임 정보 개발자는 런타임 오류 (또는 런타임 전용 상황과 관련된 동작)를 조사하는 데 필수적이라고 생각합니다.

처음 두 가지 범주의 경우 이상적인 로깅 프레임 워크는 빌드 후 프로세스로 처리하고 개발자에게 투명하게 처리해야합니다. 다음과 같이 어셈블리에 로깅을 선언적으로 추가하는 것이 좋습니다.

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

세 번째 범주의 경우 프로그래머는 하나 이상의 전용 "로깅"방법을 만들고 첫 번째 범주의 추적을 활용할 수 있습니다. 로깅 방법은 추적 규칙을 적용 할 수있는 스텁 지점을 제공하는 것 이상을 수행하지 않습니다.

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