我想将日志记录添加到当前正在处理的应用程序中。我之前添加了日志记录,这不是这里的问题。
但是从面向对象语言的设计角度来看,遵循OOP和模式的最佳记录实践是什么?
注意:我目前正在C#中执行此操作,因此显然欢迎使用C#中的示例。我也想看看Java和Ruby中的示例。
编辑:我正在使用log4net。我只是不知道插入它的最佳方法是什么。
我想将日志记录添加到当前正在处理的应用程序中。我之前添加了日志记录,这不是这里的问题。
但是从面向对象语言的设计角度来看,遵循OOP和模式的最佳记录实践是什么?
注意:我目前正在C#中执行此操作,因此显然欢迎使用C#中的示例。我也想看看Java和Ruby中的示例。
Answers:
由于您是在C#中执行此操作,因此建议您查看NLog和ElMAH。使用NUGET可以很容易地安装它们。我在下面提供了一些链接,以便您获得更多信息。
就个人而言,我选择了日志记录框架(在我的情况下为Entlib,因为我使用.NET),并编写了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
。这意味着我可以使用面向方面的编程来注释这样的方法:
[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,
}
对于第三类,程序员可以只创建一个或多个专用的“记录”方法,并利用对第一类的跟踪。日志记录方法只不过是为可以应用跟踪规则的存根提供服务。