NLog简易入门

NLog简易入门

NOTE: 本文章由 赤石俊哉 翻译整理,您可以以学习交流为目的进行任意使用。如需商用请征得原作者以及译者的同意。

安装NLog

NLog可以从Nuget上下载。
你只需要安装NLog.Config包,它将会同时安装NLogNLog.Schema包。
最后将会为工程添加一个初级的设置和智能补全(Intellisense)。
使用GUI或者在程序包管理命令行中输入:

Install-Package NLog.Config

你现在就可以编译你的程序然后开始使用NLog了。

创建Log信息

NOTE【译者注】
Log就是常说的日志信息。

为了从你的程序中创建你所需要的日志消息你需要使用日志记录API。LoggerLogManager这两个类将会很经常的被使用到,它们都在NLog命名空间里面。
Logger代表了一些被命名的日志源,而且他们有可以发出日志消息的成员方法。
LogManager则是用于创建和管理Logger对象的实例。

有一点你必须要理解的很重要的是,Logger并不代表任何特定的日志输出(因此,它并不与任何特定的日志文件等相关联),它只是一个源,它通常对应你代码中的一个类。
从日志源到输出型式的映射通过配置文件配置API分开定义。
保持这样的分离是为了让你可以在代码中继续写日志声明,通过更新一处的设置可以简单地变换日志的输出型式和输出位置。

创建Logger对象

建议为每一个类单独创建一个(private static)Logger对象。就如我们之前所说的,你需要使用LogManager去创建Logger的实例。
下面的代码将会创建一个和class有着相同名字的Logger

namespace MyNamespace
{
  public class MyClass
  {
    private static Logger logger = LogManager.GetCurrentClassLogger();
  }
}

管理Logger的名字也是可以的:

using NLog;

Logger logger = LogManager.GetLogger("MyClassName");

因为Logger对象是线程安全的,所以你可以简单地创建它并存放在一个static变量中。

日志级别

每一个日志消息都用一个日志级别来修饰,它们用来识别消息的重要性和具体细节。NLog可以以Logger对象的名称和日志级别为主,路由日志消息。

NLog支持下面的日志级别:

  • Trace - 特别细节的日志,可能包含大量的信息,比如说协议的负载。这个日志级别通常只会在开发期间被启用。
  • Debug - Debug信息,比Trace的信息量要少一些,通常不会在生产环境下使用。
  • Info - 信息消息,这个是在生产环境下经常使用到的。
  • Warn - 警告消息,通常用来指示非关键性的问题,这些问题可以被恢复或者只是临时错误。
  • Error - 错误消息 - 大多数时候,他们都是Exception(异常)。
  • Fatal - 特别严重的错误!

书写日志消息

你可以简单地通过调用Logger对象的其中一个成员方法来书写日志消息。Logger类有六个成员函数,和它们的日志级别相符合:Trace(),Debug(),Info(),Warn(),Error(),Fatal()
当然,也有一个成员函数叫Log(),你可以将日志级别作为参数来调用这个方法。

using NLog;

public class MyClass
{
  private static Logger logger = LogManager.GetCurrentClassLogger();

  public void MyMethod1()
  {
    logger.Trace("Sample trace message");
    logger.Debug("Sample debug message");
    logger.Info("Sample informational message");
    logger.Warn("Sample warning message");
    logger.Error("Sample error message");
    logger.Fatal("Sample fatal error message");

    // alternatively you can call the Log() method 
    // and pass log level as the parameter.
    logger.Log(LogLevel.Info, "Sample informational message");
  }
}

日志消息也可以被参数化 - 你可以使用字符串格式,就像你在Console.WriteLine()String.Format()中一样:

using NLog;

public class MyClass
{
  private static Logger logger = LogManager.GetCurrentClassLogger();

  public void MyMethod1()
  {
    int k = 42;
    int l = 100;

    logger.Trace("Sample trace message, k={0}, l={1}", k, l);
    logger.Debug("Sample debug message, k={0}, l={1}", k, l);
    logger.Info("Sample informational message, k={0}, l={1}", k, l);
    logger.Warn("Sample warning message, k={0}, l={1}", k, l);
    logger.Error("Sample error message, k={0}, l={1}", k, l);
    logger.Fatal("Sample fatal error message, k={0}, l={1}", k, l);
    logger.Log(LogLevel.Info, "Sample informational message, k={0}, l={1}", k, l);
  }
}

TIP: 你应当尽量避免自己进行字符串格式化来替代使用NLog中内建的格式化方法(比如:连接,以及自行调用String.Format())。具体原因表现在:
格式化日志消息需要花费很长时间,所以NLog尝试将格式化操作推迟到了当日志消息需要被输出的时候。如果消息的最后处理被跳过了,由于日志记录配置,你将不需要花费时间在String.Format()上。具体参见日志记录性能最优化。

配置

虽然到现在为止我们已经学习了如何从代码中创建日志消息,但是我们还没有对我们的日志进行任何的配置输出。所以,当你运行你的测试应用程序时,你将会看到……好吧,啥都没有。现在我们打开NLog.config文件然后添加一些日志规则:

  1. <targets>部分中,添加:
<target name="logfile" xsi:type="File" fileName="file.txt" />

这将会定义一个目标,日志将会被输出到一个叫做file.txt的文件中。

  1. <rules>部分中,添加:
<logger name="*" minlevel="Info" writeTo="logfile" />

这个片段将会引导Info级别以及更高级别(包括Info,Warn,ErrorFatal)的所有日志(name="*")输出到一个已命名为logfile的目标。

注意,当你将这个在Visual Studio中输入时,你应该可以看到IntelliSense会建议属性名并且验证他们的值。最后的配置文件应该像这样:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
        <target name="logfile" xsi:type="File" fileName="file.txt" />
    </targets>

    <rules>
        <logger name="*" minlevel="Info" writeTo="logfile" />
    </rules>
</nlog>

多个目标

让我们来试试更复杂的东西吧。想象一下你想要将非常细节的日志发送到一个文件,而且你也希望在控制台窗口中看到他们,但是在控制台窗口中细节稍微少一些。下面的配置文件可以实现这样的需求:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
        <target name="logfile" xsi:type="File" fileName="file.txt" />
        <target name="console" xsi:type="Console" />
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="logfile" />
        <logger name="*" minlevel="Info" writeTo="console" />
    </rules>
</nlog>

如你所见,我们现在有多个目标,而且有多个规则来路由日志到目标。

Logger指定的路由

还有一个比较常见的应用场景则是,需要为当前正在开发的组件提供更多细节的日志,而其他的组件将会减少输出。我们就可以用这样的配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
        <target name="logfile" xsi:type="File" fileName="file.txt" />
    </targets>

    <rules>
        <logger name="SomeNamespace.Component.*" minlevel="Trace" writeTo="logfile" final="true" />
        <logger name="*" minlevel="Info" writeTo="logfile" />
    </rules>
</nlog>

第一个规则将会将以SomeNamespace.Component开头的Logger对象的Trace级别及以上级别的日志发送到日志文件。属性final=true将会使得在写入操作之后停止进一步处理。
第二个规则将会将剩下的日志发送到同一个日志文件,但是这些日志有一个约束,那就是级别需要是Info或更高。

封装器

NLog支持特殊类型的目标,从而不需要它们自己做任何日志记录,而是修改其他Logger的行为。这些特殊的目标就称之为封装器。最常用的封装器有:
* ImpersonatingWrapper - 在写入期间冒充另一个用户。
* AsyncWrapper - 提供异步操作,目标的写入的缓冲执行。
* FallbackGroup - 提供错误时回滚。
* FilteringWrapper - 用一些条件为基础从整体日志中进行筛选。
还有很多其他的封装器可以使用,具体列表你可以看这里

为了使用封装器,简单地使用一对括起来的<target />元素用来表示封装器,然后在<rules/>部分中使用封装器的名字,下面是一个例子:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <targets>
        <target name="asyncFile" xsi:type="AsyncWrapper">
            <target name="logfile" xsi:type="File" fileName="file.txt" />
        </target>
    </targets>

    <rules>
        <logger name="*" minlevel="Info" writeTo="asyncFile" />
    </rules>
</nlog>

这样文件的全部写入操作都是异步的了,将会提高调用线程的响应速度。

层级

层级提供了一个当日志被写入文件时,指定日志内容格式的方法。主要有两种层级:

  • 简单层级 - 使用层级渲染器撰写。
  • 结构层级 - 可以输出XML、CSV、以及其他复杂的格式。

简单层级就是一个字符串,在${}中嵌入一些特殊的标记。比如下面的声明将会使得每一条日志消息都是用一个格式为yyyMMddHHmmss:的日期前缀:

<target name="logfile" xsi:type="File" fileName="file.txt" layout="${date:format=yyyyMMddHHmmss} ${message}" />

高级

展开Logger对象到子对象

当我们希望将Logger对象展开到子对象的时候,我们可以使用下面的代码:

class BaseClass
{      
    protected BaseClass()
    {
        Log = LogManager.GetLogger(GetType().FullName);
    }

    protected Logger Log { get; private set; }
}

class ExactClass : BaseClass
{
    public ExactClass() : base() { }
    ...
}

在父类(BaseClass)的构造函数中,LogManger.GetLogger()方法不应该使用Type参数自变量,也就是:

protected BaseClass()
{
    Log = LogManager.GetLogger(GetType().FullName, GetType());
}

这样调用会引发一个异常。

万一ExactClass有一个默认的调用了BaseClass构造函数的构造函数,那么,System.StackOverflowException就会被引发。这是因为在异常被引发之前,ExactClass尝试着调用了BaseClassGetLogger(String, Type)来构造ExactClass

ExactClass => BaseClass => ExactClass => BaseClass => ...

ExactClass不存在默认的构造函数时,GetLogger(String, Type)方法调用不知道如何去构造ExactClass,则引发NLog.NLogConfigurationException

展开阅读全文

没有更多推荐了,返回首页