Serilog 源码解析——Sink 的实现 (2)

首先,我们看下继承关系,Logger类除继承ILogger之外,还继承ILogEventSink接口,这个继承关系看起来很奇怪,但细想也觉得正常,一个日志记录器不光可以当日志事件的发生器,也可以当其接收器。换而言之,可以将一条日志事件写到另一个日志记录器中,由另一个日志记录器记录到其他 Sinks 中。此外,该类还继承了IDisposable接口,按照逻辑需求来讲,Logger是没有东西需要释放的,其需要释放的通常是内部包含的一些对象,比如说 FileSink 如果长时间维持一个文件句柄的话,则需要在Logger回收后被动释放,因此,这导致了Logger需要维护一组待释放的对象进行释放。在Logger内部中,通过添加Action函数钩子的方式进行释放。

之后,我们会发现所有的写入日志方法直接或间接地调用上面给出的Write方法。在该方法的逻辑中,第一行用来判断日志的等级是否满足条件,也就是一类全局的过滤条件,第二行则是判断是否给出日志的输出模板。随后_messageTemplateProcessor看这个意思是解析模板和数据(暂且不明,不过多关注)。再往下,则是构造对应的LogEvent对象。最后通过Dispatch方法将日志分发到ILogEventSink。在Dispatch中,前半部分逻辑和本篇关系不大,最后通过ILogEventSink将日志消息发送出去。

看到这里,可能会有人好奇一点,Logger应该拥有一组ILogEventSink对象才对,这样才能够实现一次向多个 Sink 中写入日志信息,但Logger只维护一个ILogEventSink对象,它是怎么做到一次向多个 Sink 中写入日志的呢?我们接着往下看。

功能性 Sink

在 Serilog 的 ./Core/Sinks 文件夹中可以发现,这里面有非常多的ILogEventSink的实现类。这些实现类都不是向具体的媒介(控制台、文件等)写入日志,反而,他们都是给其他的Sink扩展新功能,典型装饰模式的一种实现。在这个文件夹下,我把部分核心功能摘录出来,如下。(v2.10.0又添加了一些其他的装饰类,这里就不过多说明了)。

class ConditionalSink : ILogEventSink { readonly ILogEventSink _warpped; readonly Func<LogEvent, bool> _condition; ... public void Emit(LogEvent logEvent) { if (_condition(logEvent)) _wrapped.Emit(logEvent); } ... }

ConditionalSink功能非常简单,它也包含了一个ILogEventSink对象,此外,还包含一个Func<LogEvent, bool>的泛型委托。这个委托可以按照LogEvent对象满足某种指定要求做过滤。从Emit函数内可以看出,只有在满足条件时才会将日志事件发送到对应的 Sink 中。它可以看成是带有条件写入的 Sink,这一点和也就是局部过滤功能实现的核心之处。

public interface ILogEventFilter { bool IsEnabled(LogEvent logEvent); }

FilteringSink所作的事情和ConditiaonalSink一样,除了 Sink 对象外,它还维护了一组ILogEventFilter数组用来指定多个日志过滤条件,而ILogEventFilter接口如上所示,其内部就是按日志对象进行过滤。而RestrictedSink内除ILogEventSink对象外,还有一个LoggingLevelSwitch对象,这个对象用来描述日志记录器能够记录的最小日志等级,所以RestrictedSink所实现的是依照日志等级的比较判断是否输出日志。

sealed class SecondaryLoggerSink : ILogEventSink { readonly ILogger _logger; readonly bool _attemptDispose; ... public void Emit(LogEvent logEvent) { ... var copy = logEvent.Copy(); _logger.Write(copy); } }

和上述其他的ILogEventSink的继承类相比,SecondaryLoggerSink在其内部并没有保留对某个ILogEventSink的引用。相反,它保留对给定的ILogger对象的引用,这种好处是我们可以让一个日志记录器作为另一个日志记录的Sink。该类另外的一个变量_attemptDispose表示该类是否需要执行内部ILogger对象的释放,之所以这样做是因为有的时候Logger对象并不一定需要释放,通常由父日志记录器所创建出来的子日志记录器不需要释放,其资源释放可以由父日志记录器进行管理。

class SafeAggregateSink : ILogEventSink { readonly ILogEventSink[] _sinks; ... public void Emit(LogEvent logEvent) { foreach (var sink in _sinks) { ... sink.Emit(logEvent); ... } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssdss.html