除此之外,还剩下AggregrateSink和SafeAggregrateSink这两个 Sink 也继承ILogEventSink接口,且内部都引用了ILogEventSink数组,且在Emit函数中基本都是对数组内的ILogEventSink对象遍历,并调用这些对象内的Emit函数。二者均在Emit函数内将所有异常捕捉起来,但AggregateSink会在捕捉后将这些异常以AggreateException异常再次抛出。这两个类与之前的类不同,它们将多个 Sink 集合起来,让外界仍以单一的 Sink 来使用。其好处在于,Logger的设计者不需要关注到底有一个还是多个 Sink,如果有多个 Sink,只需要用这两个类将多个 Sink 包裹起来,外界将这一组 Sink 当成一个 Sink 来使用。
为什么要这样设计?实际上,对Logger类来说,它并不需要关心记录的 Sink 有一个还是多个,是什么样的状态,达到什么样的条件才能记录,毕竟这些都非常的复杂。对于Logger来讲,它要做的只有一件事,只要将日志事件向ILogEventSink对象中发出即可。为达到这样的目的,Serilog 利用设计模式中的装饰模式和组合模式来降低Logger的设计负担。主要体现在两个方面。
通过装饰模式实现带有复杂功能的 Sink,通常通过继承ILogEventSink并内部保有一个ILogEventSink对象来进行功能扩展,前面所提到的ConditionalSink、FilteringSink、RestrictedSink等都属于带有扩展功能的Sink,可以看到,其构造函数均需要外界提供额外的ILogEventSink对象。 此外,这些装饰类还可以嵌套,即一个装饰类可以拥有另一个装饰类对象,实现功能的聚合。
通过组合模式将一组 Sink 以单一 Sink 对象的方式暴露出来,AggregrateSink和SafeAggregrateSink做的就是这件事。就算Logger需要将日志记录到多个Sink中,从Logger的角度来看,它也只是写入到一个ILogEventSink对象中,这让Logger设计者不需要为了到底是一个还是多个 Sink 而头疼。举个例子,假如你有一个 ConsoleSink,它的作用是将日志输出到控制台,以及一个将日志输出到文件的 FileSink。如果想利用Logger对象将日志同时输出到控制台和文件,我们只需要构建一个AggregateSink并将 ConsoleSink 和 FileSink 对象放置到其内部的数组中,再将AggregrateSink作为Logger中的ILogEventSink的对象,那么Logger能自动将日志分别记录到这两个地方。
总结以上就是整个 Sink 功能的说明,可以看到的是,这块和之前提到的 LogDemo 项目非常的像。我相信如果在之前对 LogDemo 能够理解的人在这块能够找到非常熟悉的感觉。从下一篇开始,我将开始揭露 Serilog 是如何将 LogEvent 这样的日志事件转换成最终写入到各个Sink中的字符串信息的。