伐木外观有什么意义?

有许多不同的日志库可供select,每个日志库都有自己的怪癖和优点。 (.Net示例:log4net,System.Diagnostics.TraceSource,nLog等)

自然的倾向是抽象出这些怪癖,并使用伐木外观。 (例如: Castle.Services.Logging , Common.Logging , Simple Logging Facade )这样,如果你正在使用的给定的日志框架已经过时,或者不同的日志框架已经变得stream行,那么你可以换掉执行并离开代码不变。

但有多个日志外墙可供select。 鉴于许多不同的日志实现的答案是抽象的,为什么不使用日志外观门面? 如果这听起来很荒谬,是什么让它比原来的伐木门面更荒谬? 在日志框架的顶部是什么使得一个额外的抽象层是神奇的数字?

我将主要从使用抽象来将应用程序代码与特定日志框架隔离的angular度进行讲解。 还有其他一些因素可以影响人们对日志框架的select或者对抽象的select(和要求)。

最近我花了很多时间评估各种日志框架以及第三方日志抽象。

有些人认为从特定的日志框架中隔离应用程序代码是有价值的。 你会在这里find很多这样的post, 这个和这个 (还有更多)讨论日志的地方,很多人认为日志框架应该被包装/抽象。

显然,这可以让你不被绑定到一个特定的框架。 这很重要吗? 你会真的切换出你的日志框架? 那么也有很多人不提包装或者不推荐。 如果你看看这里发布的一些日志框架包装代码的例子,你也可以看到很多例子,为什么至less有一些人不应该包装他们的日志框架!

如果你最近开始了一个项目,你可能会检查日志框架,也许可以把它缩小到两个入围者:log4net和NLog。 每个人都有自己的意见。 log4net显然是最受欢迎的,可能是那些发表意见的人的最爱。 NLog提供了非常相似的function。 从受欢迎程度来看,log4net可能是明智的select。 基于能力,他们看起来非常相似。 基于“最近的活动”(如通过博客活动检查其源代码存储库所显示的那样),NLog是明确的select。 如果你一年前必须select,那么你可以使用log4net,因为这将是“安全”的select。 NLog何时发布还不清楚。 在此之后的一年,NLog经历了一个相当重要的开发周期,几天前发布了beta版本。

一年前选哪个? 现在选哪个? 那么是一个明显更好的select吗? 现在是更好的select吗?

抽象得到你的一件事是能够推迟决定select哪一个(你甚至不一定要selectEVER,尽pipe你可能想要如果你打算用你的产品交付日志框架)。 您可以testing一个驱动器,然后testing另一个驱动器,并了解它们如何与您的应用程序以及您的团队在您的环境中一起工作。 使用诸如Common.Logging或SLF之类的东西,你可以开始编写代码,编写一些日志接口/ API,并获得你的日志代码。 如果您认为抽象提供的接口/ API对您的工作是足够的(为什么不能,因为它本质上与log4net和NLog提供的接口/ API相同),那么没有太多使用抽象的危险。 在开发过程中,您可能会发现一个框架或另一个框架更适合您的需求。 经过抽象编码,您可以在任何时候自由select,直到您的产品出门。

你甚至可能会想,你可能会从头开始编写一个日志库。 同样,如果您认为log4net和/或NLog的接口/ API是足够的,您可以使用类似的API来实现日志库。 如果您认为这可能是使用抽象的另一个原因。 再次,您可以开始编写代码(针对您的产品,而不是您的日志logging库),logging其他日志logging框架,直到您的“从头开始”日志logging库准备就绪。 也许你真的想要使用System.Diagnostics.TraceSource和Ukadc.Diagnostics (获得类似于log4net或NLog的输出格式化function),以便与Microsoft使用TraceSources在某些平台上实现的日志logging“更好”地整合。 用TraceSources编写一个“logging器”可能相当容易,然后编写抽象,以便将其插入到Common.Logging或SLF中。 (如果接口/ API是足够的,你可以用抽象库的接口来编写你的“logging器”,而不必编写额外的抽象层)。

有了这些有说服力的论点,为什么有人不使用抽象? 哈哈,开个玩笑吧!

如果抽象是好的,你应该写自己的还是使用现有的? 如果你自己写一个,那么显然你必须写下来。 如何做到这一点? 那么,你可能只是定义一个接口,并包装一个框架(小心,包装正确!)。 稍后,如果您决定要切换,请包装该框架。 如果你小心,你不必改变任何应用程序代码,除了可能实际创build底层框架对象的地方。 也许这是好的。 您已经避免了对一些第三方抽象的依赖,因为在单个框架上实现单个包装的“小”价格。 但是,有一个成本。 在写完抽象之前,除非你有一个很好的策略来改变你的抽象,否则你不可能写很多已经login的应用程序代码。 testing两个或两个以上的框架以决定哪一个更适合您,也变得更加困难。 每个你想要“尝试”的框架都需要另一个包装工作。 如果您想轻松地在框架之间切换(至less在开发周期中),您可以轻松地完成工作。 第三方框架开箱即用。

哇! 现在我卖了! 给我伐木抽象,或给我死亡!

logging抽象所有肉汁? 有缺点吗? 他们不能那么好,可以吗?

那么和往常一样,当“买东西”或什么时候获得免费的东西的时候,你会得到可用的东西。 logging抽象也不例外。 Common.Logging和SLF都不公开log4net / NLog的至less一个非常重要的function – 日志上下文function(GDC,MDC,NDC)。 这些可能是获取足够的信息logging和格式化的关键,使您能够从中获得最大的价值。 SLF不提供TraceSource抽象。 它也不提供IsXXXEnabled函数。 Common.Logging提供了一个TraceSource抽象。 Castle.Logging是否暴露GDC / MDC / NDC的log4net和NLog。 它还提供了一个TraceSource抽象。 Castle的TraceSource抽象还通过提供“分层”命名function来增强TraceSource日志logging,类似于log4net和NLog提供的命名function。 它看起来很酷!

而且,这些项目都是一种或另一种forms的开源。 因此,取决于抽象,开发人员可能或多或less有一个既定的利益,以保持最新和增加新的function。 Common.Logging已经通过几个版本,在Spring.Net中使用AFAIK。 似乎至less在历史上是合理的。 Castle.Logging在Castle框架中使用。 因此,他们显然有“真正的”客户,并获得“现实世界”的使用,这将有望推动更多的function实施。 据我所知,SLF并不是作为“真正的”开发平台的一部分,所以很难说它的运行有多less。

目前还不清楚这些平台的路线图是什么。 Common.Logging在其网站上列出了一些即将推出的function,但是没有明确的指示何时可用。 网站上说“六月”,但是什么年份? 邮件列表多久监视一次? 对于SLF,他们的codeplex多长时间进行一次监控? 这些“免费”项目的优先级与开发者的有偿工作相比,在哪里? 你能负担得起一些第三方抽象来实现你需要的function吗? 如果你实施了一些东西,然后提交回来考虑包含在产品中,他们会接受吗?

从好的一面来看,所有这些抽象的所有来源都是可用的,所以你可以承担起它的责任,并做出任何修正或增加任何增强,而不必花费时间和精力创build一个抽象刮。 你喜欢Common.Logging,但真的想log4net / NLog GDC / MDC / NDC? 获取Castle的实现并将其添加到Common.Logging。 瞧! 一个包含几乎100%log4net / NLog日志loggingAPI的日志抽象。 你喜欢SLF但希望它有IsXXXEnabled? 没有太多的工作来实现这一点。 在您处于这个位置时,请继续前进并抓住GDC / MDC / NDC。 你喜欢城堡吗? (我不是很熟悉它,不知道在Castle之外使用它有多容易),注意,我没有使用它,但是在git上查看源代码,它看起来像NLog日志logging器抽象可能不会保留呼叫站点信息。

把多个开源项目的一部分合并成一个“超级”项目(为了你自己还是你的公司的用途)是否合乎道德? 采用Common.Logging并将其与Castle的GDC / MDC / NDC实现一起扩充会不好? 我不知道。 我会让其他人回答。

我快完蛋了

一些第三方日志抽象提供了其他function。 你可以使用一个以log4net来实现的库。 你可能不想使用log4net,或者至less可能不想被绑定到它。 Common.Logging(也许是SLF)使得捕获log4net日志消息并通过抽象重新路由它们变得相对容易,所以它们被捕获在抽象的基础日志框架的日志stream中。 SLF可能提供类似的东西。 当然,你也许可以用现有的日志框架来做类似的事情,不pipe是现成的日志框架,还是编写一个自定义的log4net Appender,NLog Target或者System.Diagnostics TraceListener。 这些特性在我对于是否在我的项目中使用第三方日志抽象的特殊评估方面并没有起到很大的作用,因为我主要是在抽象方面感兴趣。

那么,我站在哪里? 我认为保持应用程序代码与特定的日志框架绝对是有价值的。 对我来说,Common.Logging看起来像一个抽象的可靠select,虽然一些重要的function缺失(GDC / MDC / NDC),它不是Silverlight兼容。 这些function很快就可以使用了。 如果必须,我很乐意实施GDC / MDC / NDC。 使Silverlight兼容可能需要更多的努力,主要是因为我对C#/ .NET / Silverlight没有特别的经验。 在解决这些问题之前,我们可以使用Common.Logging编写大量的应用程序代码。 我们可以花时间开发我们的应用程序,而不是开发另一个日志库或抽象库。 如果我们最终不得不自己添加这些缺失的特性,那么,如果我们自己实现了一个日志库或抽象库,我们就不得不做很多事情。

我觉得在这里使得One(抽象级别)的魔术数字是Zero太less而Two太多。

在logging器外观之后交换logging器(级别数量:1)可能会导致某些用户受益,例如新logging器可以执行旧logging器不能做的事情。 我可以想象,这可能是性能,支持某些types的appender等。

更难以想象用户从交换logging器正面(层数:2)中受益。

(如果关卡的数量是0,那么这可能只是坏的面向对象的devise:在代码中引用了logging器的地方有成千上万个地方,如果下一个版本的logging器出现重大变化,会怎么样呢? )

与logging仪外墙的交易似乎是,你必须select第三方选项之一,或创build自己的,并准备坚持了很长一段时间。

直到NLog和log4net提供了一个可以用来代替具体类的接口,我总​​是把它们抽象到我自己的接口和包装类中。

为什么?

要获得所有用户要求的最大testing覆盖率,特别是当这些要求覆盖日志logging时。

当你通过一个接口而不是一个具体的类来完成所有的日志logging的时候,通过日志接口提供一个模拟对象变得非常容易(select你如何做的select,dependency injection等)来logging所有的在特定testing场景期间进行的日志logging调用。

这些可以被声明,如果代码更改打破logging你的unit testing将覆盖它。

如果NLog或者log4Net为他们的logging器提供一个接口,那么我不需要去提供一个接口和包装类,因为我可以嘲笑他们的接口进行testing。

这不是神奇的数字,这取决于你想成为多么灵活。 如果你想在某天改变日志外观,你应该写一个外观外观。 如果你只想改变日志,你需要一个门面。 如果你不想什么,不要使用门面。

你所说的缺点是特殊的能力。 如果你正在使用他们,只写你自己的门面。

在我的项目中,我使用System.Diagnostics.Trace.TraceError(...)System.Diagnostics.Debug.Print(...)作为正面的日志。 为了组织(写)日志我使用NLog,即在app.config我有configurationNLog和redirect.net的跟踪到NLog。

 <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="file" xsi:type="File" layout="${longdate:universalTime=true}Z [${threadid}] ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}" fileName="${basedir}/App_Data/logfile.txt"... </targets> </nlog> <system.diagnostics> <trace> <listeners> <add name="nlog" type="NLog.NLogTraceListener, NLog" /> </listeners> </trace> </system.diagnostics> 

这不会把我捆绑到任何logging器。 当我将组件发货给客户时,他们可以使用任何他们喜欢的logging器。 在应用程序中使用特定的logging器可能会导致问题,即您可以使用nlog,但您的客户使用log4net。

logging外观的一个重要用途是当你写一个库时 。 在库中embedded依赖关系总是需要小心,logging甚至更多。

总之, 你不想强制你的日志实施到你的图书馆用户 。 使用精心挑选的外观意味着他们将能够使用自己的框架来处理图书馆的日志,而不必经历一些奇怪的依赖排除和漏洞,使您的日志框架和他们的共存。

在这个例子中,你只需要一个抽象级别就可以换出你的logging器。

什么额外的优势可以换出你的login门面让你?

它可以在插件系统的协议中使用。 而不是说use Some3rdParty.dll and MyPlugingApi.dll我只会文档MyPlugingApi.dll 。 日志外观界面将build议并logging一些用法,可能会导致可读的日志和足够好的日志logging性能。 当然,修改不会导致更改插件API。

为什么需要改变执行? 这可能会发生,如果目前似乎开始缓慢configuration或缓慢写入条目,希望扩大到减less没有所需的第三方的.NET版本,与其他使用其他日志编写器的代码库集成。

我也在这个答案中写了另一个门面 ,这是思想的小孩。

我自己并不是专家,但是在我们这个小组里面,门面的价值并不是让我们有能力改变测井的框架。 诚然,这是我们得到的东西,但是我们处于不太可能改变我们的框架的范畴。

在我们的例子中,我们使用一个Facade来定制日志接口来满足我们应用程序的需求。 我们发现,在所有我们所看到的语义框架中,他们仍然过于关注我们所谓的“取证”模型,他们正在挖掘日志,寻找一些输出行,试图分析一些事件。

虽然这也是我们的用例,但实际上并不是我们的主要用例。 我们需要更多的工具框架,而不是日志工具框架,这将使我们能够报告感兴趣的事情 – 甚至是我们在实施时没有想到的事情。

例如,我们的应用程序不需要“消息”伴随一个事件; 相反,我们的“logging器”将接受定义事件types和状态对象的枚举(例如时间戳或其他值),并将这些枚举序列化以便于报告,性能分析,业务价值度量标准等等,除了支持传统取证。 (事实上​​,我们认识到,为了简单易用的日志界面,我们实际上使传统的取证更难一些,这增加了我们实际使用它并更频繁地使用它的可能性。

所以为了简明扼要地给出一个简要的答案,下面是我们从使用测井外观中得到的好处的大致顺序。

  1. 一致的,专用的界面促进仪器仪表(而不仅仅是传统的日志logging,如上所述)
  2. 可嘲笑的testing点
  3. 注入式logging器实现适合从本地开发到AWS S3或数据库连接,取决于部署(我们的应用程序使用Autofac与构造函数dependency injection)
  4. 可替代的logging器框架使我们能够在未来改变为不同的logging器框架。 (顺便说一下,我们不希望发生这种情况,所以,它本身并不能说服我们使用门面。)

最后,0个外墙将失去这些好处,2个外墙不会增加任何这些好处,所以这就是为什么1个外墙对我们来说是正确的。

好问题,@ brian! 🙂