.NET-Logging

.NET-Logging

在.NET框架中,日志记录(Logging)是开发过程中非常重要的一个方面,它帮助开发者跟踪应用程序的运行状态、调试问题以及监控应用性能

日志级别:

  • Trance
  • Debug
  • Information
  • Warning
  • Error
  • Critical

日志的输出方式可以是多种多样的,他可以打印到控制台,也可以记录到数据库,同样可以保存到文件中

而决定日志输出方式的是:日志提供者(LoggingProvider)

.NET同样针对与日志做了解耦,日志提供者来决定日志的推送目标,而开发人员只需要操作推送日志

就如同配置文件中,IOption只需要注入,并且将配置文件绑定到配置文件属性类,而不需要决定数据文件的来源到底是来自于控制台还是文件还是环境变量


控制台

依赖注入:

Microsoft.Extensions.Logging(被依赖传递,无需导入)

Microsoft.Extensions.Logging.Console

Logging包依赖传递了DI包,所以无需额外安装DI包

static void Main(string[] args)
{
    ServiceCollection serviceCollection = new ServiceCollection();
    // 由Logging框架提供的扩展方法
    // 通过Lambda表达式注入参数为,日志实现方式为控制台
    serviceCollection.AddLogging(builder => { builder.AddConsole(); });
}
依赖注入

与配置文件不同的是,ILogger接口被Logging包包含了

在配置文件中,我们需要额外的导入一个Options包去使用Option接口

// 注入依赖
private readonly ILogger<Test1> _logger;

public Test1(ILogger<Test1> logger)
{
    _logger = logger;
}

public void test()
{
    // 直接调用打印日志的方法
    _logger.LogDebug("开始执行数据库同步");
    _logger.LogDebug("连接数据库成功");
    _logger.LogWarning("查找数据库失败,重试第一次");
}
日志打印级别
static void Main(string[] args)
{
    ServiceCollection serviceCollection = new ServiceCollection();
    // 由Logging框架提供的扩展方法
    // 通过Lambda表达式注入参数为,日志实现方式为控制台
    serviceCollection.AddLogging(builder =>
    {
        builder.AddConsole(); 
        // 调用SetMinimumLevel设置日志输出级别为Trace(最低级别)
        // 这里LogLevel是一个枚举类型,获取到它的属性Trace
        builder.SetMinimumLevel(LogLevel.Trace);
    });
}

事件查看器(Event Log)

这是一个只支持windows的日志记录应用

依赖安装:

Microsoft.Extensions.Logging(被依赖传递,无需导入)

Microsoft.Extensions.Logging.EventLog

static void Main(string[] args)
{
    ServiceCollection serviceCollection = new ServiceCollection();
    // 由Logging框架提供的扩展方法
    // 通过Lambda表达式注入参数
    serviceCollection.AddLogging(builder =>
    {
        // 将日志打印到windows的事件查看器
        builder.AddEventLog();
        // 将日志打印到控制台
        builder.AddConsole(); 
        // 调用SetMinimumLevel设置日志输出级别为Trace(最低级别)
        // 这里LogLevel是一个枚举类型,获取到它的属性Trace
        builder.SetMinimumLevel(LogLevel.Trace);
    });
}

因为其兼容性问题,所以不建议使用


日志文件

日志文件按照时间区分(一般是一天一个文件)

  • 避免了单个文件过大的问题
  • 方便更好的定位问题

为了防止日志文件过大,有以下解决方法

  • 限制日志总个数
  • 限制日志总大小

.NET没有内置文本日志提供者,但很多第三方实现了这个服务

  • Log4Net
  • Nlog
  • Serilog

这项框架都是基于Microsoft.Extensions.Logging的封装,所以依赖传递此框架


Nlog*

依赖注入

NLog.Extensions.Logging

Microsoft.Extensions.Logging(被依赖传递,无需导入)

此依赖的配置文件默认约定在项目根目录的nlog.config文件

可以通过单独配置的方式更改此配置文件的位置和文件名,但配置文件应该遵守一条规则:约定大于配置


配置文件

配置文件 ·NLog/NLog 维基 (github.com)

<?xml version="1.0" encoding="utf-8" ?>
<!-- NLog配置文件根元素 -->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- 配置日志目标(Target) -->
    <targets>
        <!-- 定义一个名为'logfile'的目标,类型为'File',将日志输出到'file.txt'文件中 -->
        <target name="logfile" xsi:type="File" fileName="file.txt" />
        
        <!-- 定义一个名为'logconsole'的目标,类型为'Console',将日志输出到控制台 -->
        <target name="logconsole" xsi:type="Console" />
    </targets>

    <!-- 配置日志记录规则(Rule) -->
    <rules>
        <!-- 定义一条规则,名称为'*'(匹配所有日志),最小级别为'Info',写入目标为'logconsole' -->
        <logger name="*" minlevel="Info" writeTo="logconsole" />
        
        <!-- 定义另一条规则,名称为'*'(同样匹配所有日志),最小级别为'Debug',写入目标为'logfile' -->
        <logger name="*" minlevel="Debug" writeTo="logfile" />
    </rules>
</nlog>

日志过滤

简单的日志过滤当然可以通过builder.SetMinimumLevel(LogLevel.Trace);来实现

但复杂的日志过滤就必须依赖于Nlog本身来实现了

通过nlog配置文件中的rules标签的logger子标签中minlevel和maxlevel属性,来决定日志打印的级别范围

通过name属性来决定日志打印的模块范围,name属性的值应该是命名空间,来决定打印具体某一个模块的日志

为了避免Nlog与.NET 框架本身的日志输出功能冲突,所以尽量只使用一个文件输出功能,不用一起使用

例如,我们想将日志推送需要第三方服务来提供provider,但Nlog的target就可以很简单的实现此功能

<!-- 配置日志记录规则(Rule) -->
<rules>
    <!-- 定义一条规则,名称为'*'(匹配所有日志),最小级别为'Info',写入目标为'logconsole' -->
    <logger name="*" minlevel="Info" writeTo="logconsole" />

    <!-- 定义另一条规则,打印范围时service.*,最小级别为'Debug',写入目标为'logfile' -->
    <logger name="service.*" minlevel="Debug" writeTo="logfile" />
</rules>

文件设置

<!-- 通过archiveAboveSize属性,配置单个文件大小不能超过给10000个字节 -->
<!-- 通过maxArchiveFiles属性,配置最多存在于5个文件(旧文件会被循环删除) -->
<target name="logfile" xsi:type="File" archiveAboveSize="10000" maxArchiveFiles="5" fileName="file.txt" />
maxArchiveDays

通过maxArchiveDays替换maxArchiveFiles,实现文件以日期为循环删除文件,比如该属性的值为30,即最多保存一个月的日志文件

<!-- 通过archiveAboveSize属性,配置单个文件大小不能超过给102400个字节 -->
<!-- maxArchiveDays,配置最多存在于30天(旧文件会被循环删除) -->
<!-- 通过一系列规则来制作对日志文件的文件格式要求,和命名格式要求 -->
<target name="logfile" xsi:type="File" createDirs="true" keepFileOpen="true"
        fileName="${gdc:logDirectory:whenEmpty=${baseDir}}/logs/${shortdate}/Whiteboard.log"
        archiveFileName="${gdc:logDirectory:whenEmpty=${baseDir}/logs/${shortdate}}/Whiteboard_{##}.log"
        archiveAboveSize="102400" archiveNumbering="Sequence" maxArchiveDays="30"
        layout="${longdate} | ${level:uppercase=false:padding=-5} | ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/>

Serilog

相比于Nlog,Serilog更加扩展,并且功能更加强大,配置结构化日志更为方便

但Nlog更为轻量化,配置起来更简单一些,但配置结构化日志和中心化日志更为麻烦

依赖导入

Serilog.AspNetCore

Microsoft.Extensions.Logging(被依赖传递,无需导入)


中心化日志

同配置文件概念相同,如果服务器为集群式架构,可以单独部署一台服务器为日志服务器,这样可以把日志进行集中管理

对于集群部署的服务器提供商,他们都有提供自己的日志云服务,所以自己无需开发


Exceptionless

Exceptionless是控制日志数据的框架,他可以直接通过指定端口访问网页页面

它有三种调用形式:

  • 直接通过Logging调用
  • 通过Nlog调用
  • 通过Serillog调用