답변:
내가 일하는 곳에서 많은 .NET 데스크톱 앱을 작성합니다. 우리는 일반적으로 컴포넌트에 2 개의 이벤트를 구현합니다. 하나는 정보 로깅 용이고 다른 하나는 예외 로깅 용입니다 (단, 별도의 이벤트를 발생시키는 대신 예외가 발생하지만 상황에 따라 다릅니다). 이 아키텍처를 사용하면 어떠한 라이브러리도 로깅이 구현되는 방법이나 정보의 사용, 저장 또는 처리 방법을 알 필요가 없습니다. 그런 다음 응용 프로그램이 해당 응용 프로그램에 적합한 방식으로 로깅 이벤트를 처리하도록합니다. 몇 년 전이 아키텍처는 MS Enterprise Library 로깅 사용에서 BitFactory의 로깅 구성 요소로의 전환을 매우 간단한 전환으로 만들었습니다.
C # 에서이 작업을 수행하므로 NLog 및 ElMAH를 보는 것이 좋습니다. NUGET을 사용하여 매우 쉽게 설치할 수 있습니다. 더 자세한 정보를 얻을 수 있도록 아래 링크를 추가했습니다.
개인적으로, 나는 선택한 로깅 프레임 워크 (내 경우에는 .NET으로 작업하기 때문에 Entlib)를 사용하고 로깅을위한 AOP 측면을 작성합니다.
그런 다음 모든 메소드 / 속성 / 클래스 / 네임 스페이스의 속성을 지정하고 소스를 어지럽히 지 않고 로깅을 추가 할 수 있습니다.
현재 작업중 인 시스템은 이벤트 중심 아키텍처 및 메시징을 사용하므로 시스템의 대부분의 작업은 명령의 결과이며 표준 위임 이벤트가 아닌 디스패치 된 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));
}
}
다른 사람이 말했듯이, 사용 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)]
어떤 프레임 워크 가이 작업을 수행하는지 확실하지 않지만 디자인 관점에서 정보를 주로 세 가지 범주로 로그인해야한다고 모델링합니다.
처음 두 가지 범주의 경우 이상적인 로깅 프레임 워크는 빌드 후 프로세스로 처리하고 개발자에게 투명하게 처리해야합니다. 다음과 같이 어셈블리에 로깅을 선언적으로 추가하는 것이 좋습니다.
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,
}
세 번째 범주의 경우 프로그래머는 하나 이상의 전용 "로깅"방법을 만들고 첫 번째 범주의 추적을 활용할 수 있습니다. 로깅 방법은 추적 규칙을 적용 할 수있는 스텁 지점을 제공하는 것 이상을 수행하지 않습니다.