.NET中的被动日志记录是否可行?

我经常对我必须包含在我的代码中的日志记录数量感到沮丧,这让我想知道是否有更好的做事方式。

我不知道这是否已经完成或是否有人提出了更好的想法,但我想知道是否有人知道将“记录器”“注入”应用程序,以便被动地监视线程并安静地记录日志流程发生时不必执行以下操作:

public void MyProcess(int a, string b, object c) { log( String.Format( "Entering process MyProcess with arguments: [a] = [{0}]; [b] = [{1}]; [c] = [{2}]", a.ToString(), b, c.ToString() ); try { int d = DoStuff(a) log( String.Format( "DoStuff({0}) returned value {1}", a.ToString(), d.ToString() ) ); } catch (Exception ex) { log( String.Format("An exception occurred during process DoStuff({0})\nException:\n{1}", a.ToString(), ex.ToString()) ) } } 

如果我可以对我的记录器说:

 Monitor(MyClass.MyMethod) 

然后它会监视该方法内部的所有内容,包括传入参数以及方法调用和传递给这些方法的值,发生的exception等。

过去有没有人实现这样的东西? 甚至可以实施吗? 以这种方式登录只是一个白日梦吗?

我喜欢设计能做到这一点的东西,但我甚至不知道我会从哪里开始。 当然,我也不想重新发明轮子,如果它已经完成了,如果有人能指出我正确的方向,那就太好了。

任何建议都会感激不尽……

编辑:我想我会回答一个问题,询问日志中所需的详细程度。 通常需要提供可配置级别的日志记录,以便如果配置指定详细日志记录,则记录所有内容,而如果配置了关键日志记录,则仅记录某些信息以及exception。 如果配置了致命日志记录,则只会记录导致应用程序死亡的信息。 这样的东西是可配置的还是AOP需要3或4个不同的构建,具体取决于日志记录级别的数量?

我经常使用4个级别:致命,严重,信息,详细

您可以使用PostSharp来记录方法的“周围”。 这正是AOP擅长的事情。 您可能希望从Log4PostSharp开始 – 一个专门用于日志记录的插件。

这是面向方面编程的经典示例。 有关非常好的基于CLR的库,请参阅PostSharp。

这是教科书之一(不确定哪个教科书中有AoP,但你明白了)AoP的例子 – 记录:你想在方法之前和之后粘贴一些东西。

您可能想要探索AoP路线, PostSharp是受欢迎的路线之一,还有Microsoft Unity(IoC),Castle。

AoP的一个简单示例是在方法之前和之后添加代码,而不是在实际方法中添加方法调用。 正如您使用C#标记了问题一样,您可能只想研究一下扩展方法来记录它,这已经存在于这个问题中 。

我会采取切实可行的方法:你做了多少实际的日志记录? 你能不能只使用一种扩展方法,而不是让那些阅读你代码的人眼花缭乱。 内置于.NET框架中的日志记录已经很好了。

除了Jon提到的日志记录方法之外,它还可能值得注意VS中用于跟踪程序流的另一个有用的function,那就是能够使用非破坏断点来简单地输出消息或在命中时运行宏(注意你也可以打印变量值)

右键单击断点并选择When Hit …上下文菜单项。

当然,另一个非常有用的function是System.Diagnostics中的Trace对象和Trace Listners。

我最近编写了一个日志记录库,它使用IDisposable接口来包装具有日志记录上下文的区域。 基本上,你有一个像这样使用的LogSite一次性对象:

 using(var logger = new LogSite("methodName", new object[] { p1, p2, p3 }) { // code that does stuff goes here } 

LogSite对象为构造函数(如MethodBase)提供了许多方便的重载,因此您只需使用MethodBase.GetCurrentMethod()并使用reflection来获取方法和参数的实际名称(而不是硬编码字符串)。

它的工作方式是这样的 – 在构造函数中,它使用所有跟踪信息写入日志,以指示它进入块。 在Dispose方法中,它写入一个退出条目。

在处理时,它还检查Marshal.GetExceptionCode()是否为非零值,以查看使用中的代码是抛出exception还是正常退出。 它没有给你exception,因此必须在catch处理程序中显式记录,但它确实指示该区域的“通过/失败”。 这允许您的日志记录范围比方法更具体,因为您可以在单个方法中拥有大量这些块,并且知道哪个块完全抛出了exception。

此外,由于现在有一个“logger”对象,你的catch处理程序看起来像:

 try { ... } catch (Exception ex) { logger.LogException(ex); } 

记录器已经知道方法名称,参数信息以及所有这些,并且具有用于格式化exception信息的内部方法。

进入这个高级对象下面的架构,有一个“LogDisposition”的概念,它处理我们之前确定的“通过/失败”,并且有一个“LogEntryType”的概念,它是一个filter(用Flags枚举实现),表示传递什么类型的日志条目(错误,跟踪等)。

实际执行日志记录的事情只是发布者/侦听器模式。 发布者接受传入的日志条目,就像多播委托一样,保留LogListener实例的注册表(应在程序开始时设置,或根据需要动态添加),并将日志条目传递给这些实例。

反过来,LogListeners过滤掉他们关心的日志条目类型。因此,如果您不希望方法入口和出口点用于非错误条件,则它们不必显示在日志中。 这可以在运行时进行控制,以允许用户执行诸如随意打开和关闭详细日志记录之类的操作。 由于发布者可以写入各种loglisteners,您可以连接写入文件或写入数据库的内容,或在GUI中显示错误通知等。

这是一个非常好的系统,需要相对少量的编码才能获得相对丰富的日志记录。

如果你愿意,我可以给你一个代码示例…你可以通过我的(几乎完全不活跃的)博客联系我(参见我的帐户资料)。

希望有所帮助。

我在所有项目中都使用了开源Apache log4net 。 这是一个非常简单的实现,具有各种扩展,允许您登录到数据库,zip文件,滚动日志文件,RRS订阅源,telnet客户端等。日志记录基本上就像:

 'Will print stack trace after message' log.err(ex.message,ex) log.warn("warn") log.info("info") log.debug("debug") 

当应用程序处于运行时,记录参数(例如输出格式和日志级别)都会实时读取。