扩展Nlog Layout Renderer原理、Demo、 工具下载

本文介绍如何为NLog创建自定义LayoutRenderer扩展,包括原理分析、代码示例及配置方法,帮助开发者轻松实现特定的日志记录需求。

我们可以通过修改NLog配置文件来控制日志输出格式,但是有时候我们会有这样的需求:我要把UserID OrderID也记录下来,我们可以像使用${date}${message}一样写${UserID} ${OrderID}么?答案是可以的,只需要写一个扩展。我们讲分析实现原理,给出代码示例,演示一下怎么在程序中应用,并提供一个生成Layout Renderer扩展代码的小工具。

Layout Renderer扩展的原理

作为一个有着良好扩展性的日志组件,我们要实现上述目标也是不必修改NLog源代码的。这很好的符合了面对对象的OCP原则。不过要分析原理还是要从源代码下手,打开“\Log4WF\NLog-1.0\src\NLog\LayoutRenderers”文件夹,这里你可以看到NLog默认的提供的输出标记的实现。

打开一个ThreadID.cs的文件,我们看一下

1 None.gif using System;
2 None.gif using System.Text;
3 None.gif using System.Runtime.InteropServices;
4 None.gif
5 None.gif using NLog.Internal;
6 None.gif
7 None.gif namespace NLog.LayoutRenderers
8 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
9ExpandedSubBlockStart.gifContractedSubBlock.gif/**////<summary>
10InBlock.gif///Theidentifierofthecurrentthread.
11ExpandedSubBlockEnd.gif///</summary>

12InBlock.gif[LayoutRenderer("threadid")]
13InBlock.gifpublicclassThreadIDLayoutRenderer:LayoutRenderer
14ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
15ExpandedSubBlockStart.gifContractedSubBlock.gif/**////<summary>
16InBlock.gif///Returnstheestimatednumberofcharactersthatareneededto
17InBlock.gif///holdtherenderedvalueforthespecifiedloggingevent.
18InBlock.gif///</summary>
19InBlock.gif///<paramname="logEvent">Loggingeventinformation.</param>
20InBlock.gif///<returns>Thenumberofcharacters.</returns>
21InBlock.gif///<remarks>
22InBlock.gif///Iftheexactnumberisnotknownor
23InBlock.gif///expensivetocalculatethisfunctionshouldreturnaroughestimate
24InBlock.gif///that'sbigenoughinmostcases,butnottoobig,inordertoconservememory.
25ExpandedSubBlockEnd.gif///</remarks>

26InBlock.gifprotectedinternaloverrideintGetEstimatedBufferSize(LogEventInfologEvent)
27ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
28InBlock.gifreturn32;
29ExpandedSubBlockEnd.gif}

30InBlock.gif
31ExpandedSubBlockStart.gifContractedSubBlock.gif/**////<summary>
32InBlock.gif///Rendersthecurrentthreadidentifierandappendsittothespecified<seecref="StringBuilder"/>.
33InBlock.gif///</summary>
34InBlock.gif///<paramname="builder">The<seecref="StringBuilder"/>toappendtherendereddatato.</param>
35ExpandedSubBlockEnd.gif///<paramname="logEvent">Loggingevent.</param>

36InBlock.gifprotectedinternaloverridevoidAppend(StringBuilderbuilder,LogEventInfologEvent)
37ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
38InBlock.gifbuilder.Append(ApplyPadding(ThreadIDHelper.Instance.CurrentThreadID.ToString()));
39ExpandedSubBlockEnd.gif}

40ExpandedSubBlockEnd.gif}

41ExpandedBlockEnd.gif}

42 None.gif

这里重载了两个方法:GetEstimatedBufferSize设置缓冲区大小;Append将要记录的内容追加到日志中。

这也给我们指明了方向,其实我们要做的是两件事情:

1.估计缓冲区大小,这个不难,有些真的是无法估计缓冲区大小的怎么办?打开Message.cs看一下,可以写 logEvent.Message.Length;

2.把要标记所代表的信息追加到日志流中,追加可以很简单的模仿上面的写法,可是到哪里取我们的记录的数据呢?或者说我们要把数据放在哪里可以方便的提取呢?

LogEventInfo这个类代表了要记录的事件信息,除了Message等熟悉之外里面有一个字典类型的Context,可以在这里记录额外的信息。当然了后面我们还要从这里提取出来追加到日志中。看例子,

1 None.gif 这样LayoutRenderer的扩展也就毫无悬念了,比如我们要写的Query的扩展就会是类似下面这个样子:
2 None.gif using System;
3 None.gif using System.Collections.Generic;
4 None.gif using System.Text;
5 None.gif using NLog;
6 None.gif
7 None.gif namespace MyTest.NlogLayoutRenderers
8 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
9InBlock.gif[LayoutRenderer("Query")]//注意这里
10InBlock.gifpublicclassQueryLayloutRenderer:LayoutRenderer
11ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
12InBlock.gifprotectedoverrideintGetEstimatedBufferSize(LogEventInfologEvent)
13ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
14InBlock.gifreturnlogEvent.Message.Length;//注意这里
15ExpandedSubBlockEnd.gif}

16InBlock.gifprotectedoverridevoidAppend(StringBuilderbuilder,LogEventInfologEvent)
17ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
18InBlock.gifbuilder.Append(logEvent.Context["Query"].ToString());//注意这里
19ExpandedSubBlockEnd.gif}

20ExpandedSubBlockEnd.gif}

21ExpandedBlockEnd.gif}

22 None.gif
23 None.gif

在程序中使用自定义扩展

NLog如何使用这些自定义的标记呢?

NLog在很多场合做同一件事情都会给我们两个选择,可以用配置文件来做,同样也可以用代码来完成同等的操作。

同样这里也是又两种方式可供选择:

  • 如果只是一个简单的扩展,可以考虑使用代码加载: LayoutRendererFactory.AddLayoutRenderer("UserID", typeof(NLogTest.UserIDLayoutRenderer));

完成这样一个预备工作,就可以在后续的代码中使用自定义的UserID标记了。

  • 同样的我们可以把这些自定义的LayoutRenderer的扩展放在一个程序集里面,通过配置NLog的配置文件,可以让NLog在初始化的时候加载这些扩展。这样显然更灵活了。具体配置文件样例如下:
  • <nlog>
    <extensions>
    <addassembly="MyTest"/>
    </extensions>
    <targets>
    <targetname="console"type="Console"
    layout="${Query} ${message}"/>
    </targets>
    <rules>
    <loggername="*"minLevel="Info"appendTo="console"/>
    </rules>
    </nlog>
    这里有一个细节,extensions配置节必须要写在其它配置节的前面,否则会出错。

明白了原理,代码用工具生成吧

程序员也许是最讨厌重复的人群,于是好多人实践重构以消除重复。

写了一些LayoutRenderer的扩展之后,我们会发现代码绝大多数是相似的,变化的点只有变量名和缓冲区的长度。写一个模板,然后简单的字符串替换生成文件就可以了。没有什么技术含量,只是简化复制粘贴的过程。如果NLog的架构能把这件工作给简化了,那才是真正的简化。

工具包含这样两个东西:

1 None.gif 1 .代码模板,需要替换的字符串是@Name@Size
2 None.gif using System;
3 None.gif using System.Collections.Generic;
4 None.gif using System.Text;
5 None.gif using NLog;
6 None.gif
7 None.gif namespace MyTest.NlogLayoutRenderers
8 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
9InBlock.gif[LayoutRenderer("@Name")]
10InBlock.gifpublicclass@NameLayloutRenderer:LayoutRenderer
11ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
12InBlock.gifprotectedoverrideintGetEstimatedBufferSize(LogEventInfologEvent)
13ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
14InBlock.gifreturn@Size;
15ExpandedSubBlockEnd.gif}

16InBlock.gifprotectedoverridevoidAppend(StringBuilderbuilder,LogEventInfologEvent)
17ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
18InBlock.gifbuilder.Append(logEvent.Context["@Name"].ToString());
19ExpandedSubBlockEnd.gif}

20ExpandedSubBlockEnd.gif}

21ExpandedBlockEnd.gif}

22 None.gif

2.字段列表,每一行包含字段名和字段长度,中间使用TAB隔开

User50

Session36

IP15

Login24

Online32

LoginMethodlogEvent.Message.Length

LogoutMethodlogEvent.Message.Length

Library32

使用的时候只要修改这两个文件就可以了,生成的文件在当前目录的Output文件夹中,文件编码UTF-8

下载地址:NLogTool.rar

参考文献: <http://www.nlog-project.org/howto_write_layout_renderer.html>

(本文完)

坚强2002和你一起回头再说...
Tag标签: NLog
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值