.NET .Core 选择日志框架

摘要:
首先,让我们介绍NET:log4net、NLog和Serilog。Logger Logger是应用程序需要与之交互的主要组件。它用于生成日志消息。Repository Repository主要用于维护日志对象的组织结构。它主要包括以下类型:AnsiColorTerminalAppender:在ANSI窗口终端上写入高亮度日志事件。AspNetTraceAppender:您可以在asp.net中以Trace的方式查看记录的日志。EventLogAppender:将日志写入WindowsEventLog。FileAppender:将日志写入文件。NetSendAppender:将日志输出到WindowsMessenger服务。这些日志信息将显示在用户终端的对话框中。RemotingAppender:通过将日志写入远程接收器。NET远程处理。TraceAppender:将日志写入。NETtrace系统。

先来介绍.NET中三种最受欢迎​​的日志记录框架:log4net,NLog和Serilog。

一、Log4net

1、Log4net概述

  Log4Net有四种主要的组件,分别是Logger(记录器),Repository(库),Appender(附着器)以及 Layout(布局)

  • Logger记录器

  Logger是应用程序需要交互的主要组件,它用来产生日志消息。产生的日志消息并不直接显示,还要预先经过Layout的格式化处理后才会输出。Logger提供了多种方式来记录一个日志消息,你可以在你的应用程序里创建多个Logger,每个实例化的Logger对象都被log4net框架作为命名实体(named entity)来维护,这意味着为了重用Logger对象,可以将Logger对象当做参数使用。Log4net框架定义了一个ILog接口,所有的logger类都必须实现这个接口。如果你想实现一个自定义的logger,你必须首先实现这个接口。Log4Net框架定义了一个叫做LogManager的类,用来管理所有的logger对象。它有一个GetLogger()静态方法,用我们提供的名字参数来检索已经存在的Logger对象。如果框架里不存在该Logger对象,它也会为我们创建一个Logger对象。 

  • Repository库

  Repository主要用于负责日志对象组织结构的维护。

  • Appender(附着器)

  Appender决定日志输出的媒介。主要包括以下几种:

    • AnsiColorTerminalAppender:在ANSI 窗口终端写下高亮度的日志事件。
    • AspNetTraceAppender:能用asp.net中Trace的方式查看记录的日志。
    • BufferingForwardingAppender:在输出到子Appenders之前先缓存日志事件。
    • ConsoleAppender:将日志输出到控制台。
    • EventLogAppender:将日志写到Windows Event Log. 
    • FileAppender:将日志写到文件中。
    • LocalSyslogAppender:将日志写到local syslog service (仅用于UNIX环境下). 
    • MemoryAppender:将日志存到内存缓冲区。
    • NetSendAppender:将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。
    • RemoteSyslogAppender:通过UDP网络协议将日志写到Remote syslog service。
    • RemotingAppender:通过.NET Remoting将日志写到远程接收端。
    • RollingFileAppender:将日志以回滚文件的形式写到文件中。(实例代码中使用的是此类型)
    • SmtpAppender:将日志写到邮件中。
    • TraceAppender:将日志写到.NET trace 系统。
    • UdpAppender:将日志connectionless UDP datagrams的形式送到远程宿主或以UdpClient的形式广播。   
  • root

    Appender和root经常搭配使用,root用来对设置输出的方式进行指定。
<root>
    <!--批定DEBUG输出的文件形式记录日志-->
    <level value="DEBUG"/>
    <appender-ref ref="Log4Net_ERROR" />
  
  <!--批定INFO输出的文件形式记录日志-->
    <level value="INFO"/>
    <appender-ref ref="Log4Net_INFO" />
</root>
  • Appender Filters过滤器

  Appender的过滤器(Appender Filters) 可以按照不同的标准过滤日志事件。在log4net.Filter的名字空间下已经有几个预定义的过滤器。使用这些过滤器,你可以按照日志级别范围过滤日志事件,或者按照某个特殊的字符串进行过滤。过滤器通常有以下几种:

    • DenyAllFilter 阻止所有的日志事件被记录
    • LevelMatchFilter 只有指定等级的日志事件才被记录
    • LevelRangeFilter 日志等级在指定范围内的事件才被记录
    • LoggerMatchFilter 与Logger名称匹配,才记录
    • PropertyFilter 消息匹配指定的属性值时才被记录
    • StringMathFilter 消息匹配指定的字符串才被记录
  • Layout布局

  Layout 组件用于向用户显示最后经过格式化的输出信息。输出信息可以以多种格式显示,主要依赖于我们采用的Layout组件类型。可以是线性的或一个XML文件。Layout组件和一个Appender组件一起工作。API帮助手册中有关于不同Layout组件的列表。一个Appender对象,只能对应一个Layout对象。要实现你自己的Layout类,你需要从log4net.Layout.LayoutSkeleton类继承,它实现了ILayout接口。一个Appender只能有一个Layout。最常用的Layout应该是经典格式的PatternLayout,其次是SimpleLayout,RawTimeStampLayout和ExceptionLayout。然后还有IRawLayout,XMLLayout等几个使用较少。Layout可以自己实现,需要从log4net.Layout.LayoutSkeleton类继承,来输出一些特殊需要的格式,在后面扩展时就重新实现了一个Layout。

      • SimpleLayout简单输出格式,只输出日志级别与消息内容。
      • RawTimeStampLayout 用来格式化时间,在向数据库输出时会用到。样式如“yyyy-MM-dd HH:mm:ss“
      • ExceptionLayout需要给Logger的方法传入Exception对象作为参数才起作用,否则就什么也不输出。输出的时候会包含Message和Trace。
      • PatterLayout使用最多的一个Layout,能输出的信息很多
  • 配置文件说明

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!--添加自定义节点:log4net  type:解析类名,程序集名(log4net.dll)-->
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>

  <log4net>
    <!--定义输出到文件中-->
    <appender name="Log4Net_INFO" type="log4net.Appender.RollingFileAppender">
      <!--定义文件存放位置-->
      <file value="C:/log4net/"/>
      <!--是否追加到文件,默认为true,通常无需设置-->
      <appendToFile value="true"/>
      <RollingStyle value="Date"/>
      <!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
      <DatePattern value="INFO_yyyyMMdd".log"" />
      <!--日志文件名是否为静态-->
      <StaticLogFileName value="false"/>
      <!--多线程时采用最小锁定-->
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <!--布局(向用户显示最后经过格式化的输出信息)-->
      <layout type="log4net.Layout.PatternLayout">
        <!--
           %m(message):输出的日志消息,如ILog.Debug(…)输出的一条消息 
           %n(new line):换行 
           %d(datetime):输出当前语句运行的时刻 
           %r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数 
           %t(thread id):当前语句所在的线程ID 
           %p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等 
           %c(class):当前日志对象的名称,例如:
           %L:输出语句所在的行号 
           %F:输出语句所在的文件名 
           %-数字:表示该项的最小长度,如果不够,则用空格填充
          -->
        <Header value="[Header]
"/>
        <Footer value="[Footer]
"/>
        <!--正文-->
        <ConversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 出错类:%logger property:[%property{NDC}] - 错误描述:%message%newline"  />
      </layout>
    </appender>

    <appender name="Log4Net_ERROR" type="log4net.Appender.RollingFileAppender">
      <file value="C:/log4net/"/>
      <appendToFile value="true"/>
      <RollingStyle value="Date"/>
      <DatePattern value="ERROR_yyyyMMdd".log"" />
      <StaticLogFileName value="false"/>
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.PatternLayout">
        <Header value="[Header]
"/>
        <Footer value="[Footer]
"/>
        <!--正文-->
        <ConversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 出错类:%logger property:[%property{NDC}] - 错误描述:%message%newline"  />
      </layout>
    </appender>

    <root>
      <level value="DEBUG"/>
      <appender-ref ref="Log4Net_ERROR" />

      <level value="INFO"/>
      <appender-ref ref="Log4Net_INFO" />
    </root>

  </log4net>

</configuration>

2、自定义Logger示例

定义一个类库Log4netManager,通过nuget引用log4net组件,类库结构如下:

.NET .Core 选择日志框架第1张

ELogLevel.cs代码如下:

namespace Log4netManager
{
    public enum ELogLevel
    {
        /// <summary>
        /// 错误信息
        /// </summary>
        Error=1,
        /// <summary>
        /// 跟踪信息
        /// </summary>
        Trace=2,
        /// <summary>
        /// 调试信息
        /// </summary>
        Debug=3,
        /// <summary>
        /// 记录信息
        /// </summary>
        Info=4
    }
}

ILog.cs代码如下:

using System;

namespace Log4netManager
{
    public interface ILog:IDisposable
    {
        void LogWithTime(string msg, ELogLevel logLevel = ELogLevel.Error);
        bool Enabled { get; set; }
    }
}

FileLogger.cs代码如下:

using System;
using System.Diagnostics;
using System.IO;

namespace Log4netManager
{
    public class FileLogger : ILog
    {
        private string _configLevel = null;
        private bool _enabled = true;
        private log4net.ILog _log = null;
        public FileLogger()
        {
            this._configLevel = GetAppSettingValue("LogLevel");
            string logName = GetAppSettingValue("LogName");
            string configPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "log4net.xml");
            log4net.Config.XmlConfigurator.Configure(new FileInfo(configPath));
            this._log = log4net.LogManager.GetLogger(logName);
        }

        public static string GetAppSettingValue(string key)
        {
            string value = null;
            foreach (string item in System.Configuration.ConfigurationManager.AppSettings)
            {
                if (item.Equals(key, System.StringComparison.CurrentCultureIgnoreCase))
                {
                    value = System.Configuration.ConfigurationManager.AppSettings[key];
                    break;
                }
            }
            return value;
        }

        #region ILog成员
        public void LogWithTime(string msg, ELogLevel logLevel)
        {
            if (string.IsNullOrWhiteSpace(msg) || !this._enabled)
            {
                return;
            }
#if DEBUG
            Trace.TraceInformation(msg);
#endif
            if (string.IsNullOrWhiteSpace(this._configLevel))
            {
                this._configLevel = ((int)ELogLevel.Error).ToString();
            }
            int configLevel = Convert.ToInt32(this._configLevel);
            if ((int)logLevel < configLevel)
            {
                try
                {
                    switch (logLevel)
                    {
                        case ELogLevel.Error:
                            this._log.Error(msg);
                            break;
                        case ELogLevel.Trace:
                            this._log.Warn(msg);
                            break;
                        case ELogLevel.Debug:
                            this._log.Debug(msg);
                            break;
                        case ELogLevel.Info:
                            this._log.Info(msg);
                            break;
                        default:
                            break;
                    }
                }
                catch
                {
                }
            }
        }
        public bool Enabled
        {
            get { return this._enabled; }
            set { this._enabled = value; }
        }
        #endregion

        #region IDisposable
        public void Dispose()
        { }
        #endregion 
    }
}

OK,接下来使用下我们自定义的Logger,先建立一个控制台项目,引入刚刚自定义的Log4netManager类库,结构如下:

.NET .Core 选择日志框架第2张

App.config代码:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
    </startup>
  <appSettings>
    <add key="LogName" value="TestConsole"/>
    <add key="LogLevel" value="4"/>
  </appSettings>
</configuration>

log4net.xml如下:

<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="true">
  <appender name="FlatFile" type="log4net.Appender.RollingFileAppender,log4net">
    <param name="File" value="logs/website.log" />
    <param name="Encoding" value="utf-8" />
    <param name="AppendToFile" value="true" />
    <param name="RollingStyle" value="Date" />
    <param name="ImmediateFlush" value="true" />
    <param name="MaximumFileSize" value="50MB"/>
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <param name="DatePattern" value="-yyyy.MM.dd'.log'" />
    <param name="StaticLogFileName" value="true" />
    <layout type="log4net.Layout.PatternLayout,log4net">
      <param name="ConversionPattern" value="%-5level [%logger] - %message [%date] %newline" />
    </layout>
  </appender> 
  <root>
    <priority value="DEBUG" />
    <appender-ref ref="FlatFile" />
  </root>  
</log4net>

Program.cs如下:

using Log4netManager;

namespace log4netConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            ILog _fileLogger = new FileLogger();
            _fileLogger.LogWithTime("日志测试",ELogLevel.Debug);
            System.Console.WriteLine("日志所在的位置 "+System.AppDomain.CurrentDomain.BaseDirectory);
            System.Console.ReadKey();
        }
    }
}

效果如下:

.NET .Core 选择日志框架第3张

3、Netcore中使用log4net

新建一个netcore mvc项目,通过nuget引入包Microsoft.Extensions.Logging.Log4Net.AspNetCore,项目结构如下:

.NET .Core 选择日志框架第4张

Program.cs代码如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace NetCoreLog
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureLogging((context, loggingbuilder) =>
                {
                    loggingbuilder.AddFilter("System", LogLevel.Warning); //过滤掉系统默认的一些日志
                    loggingbuilder.AddFilter("Microsoft", LogLevel.Warning);//过滤掉系统默认的一些日志                    
                    loggingbuilder.AddLog4Net();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

log4net.config如下:

<?xml version="1.0" encoding="utf-8"?>
<log4net>
    <appender name="RollingAppender" type="log4net.Appender.RollingFileAppender">
        <!--指定日志文件保存的目录-->
        <file value="loglog.txt"/>
        <!--追加日志内容-->
        <appendToFile value="true"/>
        <!--可以为:Once|Size|Date|Composite-->
        <!--Compoosite为Size和Date的组合-->
        <rollingStyle value="Composite"/>
        <!--设置为true,当前最新日志文件名永远为file字节中的名字-->
        <staticLogFileName value="false"/>
        <!--当备份文件时,备份文件的名称及后缀名-->
        <datePattern value="yyyyMMdd.TXT"/>
        <!--日志最大个数-->
        <!--rollingStyle节点为Size时,只能有value个日志-->
        <!--rollingStyle节点为Composie时,每天有value个日志-->
        <maxSizeRollBackups value="20"/>
        <!--可用的单位:KB|MB|GB-->
        <maximumFileSize value="5MB"/>
        <filter type="log4net.Filter.LevelRangeFilter">
            <param name="LevelMin" value="ALL"/>
            <param name="LevelMax" value="FATAL"/>
        </filter>
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
        </layout>
    </appender>
    <root>
        <priority value="ALL"/>
        <level value="ALL"/>
        <appender-ref ref="RollingAppender"/>
    </root>
</log4net>

HomeController.cs代码如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NetCoreLog.Models;
using System.Diagnostics;

namespace NetCoreLog.Controllers
{
    public class HomeController : Controller
    {
        //ILoggerFactory和ILogger都是系统内置的接口,两个都可以写日志,使用其中之一即可
        private readonly ILogger<HomeController> _logger;
        private readonly ILoggerFactory _factory;
        public HomeController(ILogger<HomeController> logger, ILoggerFactory factory)
        {
            this._logger = logger;
            this._factory = factory;
        }

        public IActionResult Index()
        {
            this._factory.CreateLogger<HomeController>().LogError("记录错误信息!!!");
            this._logger.LogError("记录错误信息!");
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

效果如下:

.NET .Core 选择日志框架第5张

二、NLog

1、NLog概述

NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码。NLog是一个简单灵活的.NET日志记录类库。通过使用NLog,我们可以在任何一种.NET语言中输出带有上下文的(contextual information)调试诊断信息,根据喜好配置其表现样式之后发送到一个或多个输出目标(target)中。NLog遵从BSD license,即允许商业应用且完全开放源代码。任何人都可以免费使用并对其进行测试,然后通过邮件列表反馈问题以及建议。 

  • NLog和Log4net的区别

  NLog的API非常类似于log4net,且配置方式非常简单。NLog使用路由表(routing table)进行配置,但log4net却使用层次性的appender配置,这样就让NLog的配置文件非常容易阅读,并便于今后维护。

  • NLog配置介绍

<?xml version="1.0" encoding="utf-8" ?>
<!-- 默认命名空间 -->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd">
  <!-- 这个命名空间里面的元素或者属性就必须要以xsi:这种方式来写,比如schemaLocation就是他的一个属性,所以写成xsi:schemaLocation。
  而默认命名空间不带类似xsi这种;其实xml标签名称有个专业叫法叫做QName,而如果没有前面的xsi:这种一般叫做NCName -->
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  <!--表示把定义这个命名空间的schema文件给引用进来,好让开发类型工具能够解析和验证你的xml文件是否符合语法规范
  简单来说 上面是用来验证你XML格式是否正确的。-->
  xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
  <!--一旦启动程序,这时候NLog.config文件被读取后,知道程序再启动都不会再读取配置文件了。假如我们不想停掉程序,比如说服务器哪能说停就停哈。这就用上这个配置了,这个配置功能是,一旦你对配置文件修改,程序将会重新读取配置文件,也就是自动再配置。-->
  autoReload="true"
  <!--NLog日志系统抛出异常-->
  throwExceptions="false"
  <!--日志级别 -->
  internalLogLevel="Debug"
  <!--NLog内部日志文件位置 -->
  internalLogFile="c:	emp
log-internal.log">
  <!--variable定义配置文件中用到的变量-->
<variable name="myvar" value="myvalue"/>
  <!--定义日志的目标/输出-->
  <targets> </targets>
  <!--定义日志的路由规则-->
  <rules> </rules>
</nlog>

2、NetCore中使用NLog

新建一个netcore  mvc项目,通过nuget引入包NLog.Config和NLog.Web.AspNetCore。在引入NLog.Config包的时候,在项目根目录自动生成一个NLog.config,将该文件属性改为始终复制。项目结构如下:

.NET .Core 选择日志框架第6张

修改下NLog.config:

<?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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:	emp
log-internal.log">

    <!-- optional, add some variables
  https://github.com/nlog/NLog/wiki/Configuration-file#variables
  -->
    <variable name="myvar" value="myvalue"/>

    <!--
  See https://github.com/nlog/nlog/wiki/Configuration-file
  for information on customizing logging rules and outputs.
   -->
    <targets>

        <!--
    add your targets here
    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
    -->

        <!--
    Write events to a file with the date in the filename.
     -->
        <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
                layout="${longdate} ${uppercase:${level}} ${message}" />

    </targets>

    <rules>
        <!-- add your logging rules here -->

        <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    -->
        <logger name="*" minlevel="Debug" writeTo="f" />

    </rules>
</nlog>

Program.cs代码如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;

namespace NetCoreNlog
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                //using NLog.Web;
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                }).UseNLog();
    }
}

HomeController.cs代码如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NetCoreNlog.Models;
using System.Diagnostics;

namespace NetCoreNlog.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

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

        public IActionResult Index()
        {
            _logger.LogInformation("这里是日志记录");
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

效果如下:

.NET .Core 选择日志框架第7张

三、Serilog

1、Serilog概述

该系列的最新日志记录框架Serilog于2013年发布。Serilog与其他框架之间的最大区别在于,该框架旨在进行结构化的现成日志记录。

2、Serilog示例

这次无需任何XML配置!Serilog的配置使用流畅的界面,使其非常美观和干净。

using Serilog;
using System;
namespace LoggingDemo.Serilog
{
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console()
                .WriteTo.File("logfile.log", rollingInterval: RollingInterval.Day)
                .CreateLogger();
            Log.Debug("Starting up");
            Log.Debug("Shutting down");
            Console.ReadLine();
        }
    }
}
The logging output from Serilog looks like:

输出结果如下:

2018-08-18 14:37:21.463 -06:00 [DBG] Starting up
2018-08-18 14:37:21.560 -06:00 [DBG] Shutting down
四、.NET .Core 使用Serilog

最佳日志记录框架:Serilog。该API更现代,更易于设置,维护更好,并且默认情况下进行结构化日志记录。添加浓缩器的功能能够拦截和修改消息,这非常有用。

1、在控制台项目中使用

前提:引入Serilog.AspNetCore包

.NET .Core 选择日志框架第8张

新建一个Serilog帮助类SerilogHelper,定义两种方法,一个是将日志输出到console,一个是将日志输出到文件

using Serilog;
using System;
using System.IO;

namespace SerilogTest
{
    public static class SerilogHelper
    {
        /// <summary>
        /// 输出到Console
        /// </summary>
        public static void WriteToConsole()
        {
            //日志的输出模板
            string Logformat = @"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3} {SourceContext:l}] {Message:lj}{NewLine}{Exception}";
            //类似创建一个管道
            Log.Logger = new LoggerConfiguration()
                //设置最低等级
                .MinimumLevel.Debug()
                //将事件发送到控制台并展示
                .WriteTo.Console(outputTemplate: Logformat)
                .CreateLogger();

            //这里因为设置了最低等级为Debug,所以比Debug低的Verbose不会展示在控制台
            Log.Verbose("Verbose级别的日志消息");
            Log.Information("计算开始");

            try
            {
                int a = 0;
                int b = 5;
                Log.Debug("计算两者相除");
                Console.WriteLine(b / a);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "计算出现意外的错误");
            }
            Log.Information("计算结束");
        }

        public static void WriteToFile()
        {
            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "log.txt");
            //日志的输出模板
            string Logformat = @"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3} {SourceContext:l}] {Message:lj}{NewLine}{Exception}";
            Log.Logger = new LoggerConfiguration()
              .MinimumLevel.Debug()
              //该行代码表示输出到console
              .WriteTo.Console(outputTemplate: Logformat)
              //第一个参数是文件路径,第二个参数是输出模板的选择,第三个参数是表示程序隔多长时间新创造一个日志文件
              .WriteTo.File(path, outputTemplate: Logformat, rollingInterval: RollingInterval.Day)
              .CreateLogger();

            //这里因为设置了最低等级为Debug,所以比Debug低的Verbose不会展示在控制台
            Log.Verbose("Verbose级别的日志消息");
            Log.Information("计算开始");

            try
            {
                int a = 0;
                int b = 5;
                Log.Debug("计算两者相除");
                Console.WriteLine(b / a);
            }
            catch (Exception ex)
            {
                Log.Error(ex, "计算出现意外的错误");
            }
            Log.Information("计算结束");
        }
    }
}

在Program调用方法

复制代码
namespace SerilogTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //SerilogHelper.WriteToConsole();
            SerilogHelper.WriteToFile();
        }        
    }
}
复制代码

2、在web中使用Serilog

前提:引入Serilog.AspNetCore包

配置appsettings.json

{
  "log": { //日志配置
    "minlevel": "Verbose", //定义详见Serilog.Events.LogEventLevel
    "console": {
      "enabled": true
    },
    "debug": {
      "enabled": true
    },
    "file": {
      "enabled": true
    },
    "elasticsearch": {
      "enabled": false,
      "nodes": [ "http://localhost:9200/" ],
      "indexformat": "colder"
    },
    "overrides": [ //重写日志输出级别
      {
        "source": "Microsoft.AspNetCore",
        "minlevel": "Warning"
      },
      {
        "source": "Microsoft.EntityFrameworkCore",
        "minlevel": "Information"
      },
      {
        "source": "Microsoft.EntityFrameworkCore.Infrastructure",
        "minlevel": "Warning"
      }
    ]
  },
  "AllowedHosts": "*"
}

新建一个对应appsettings.json的model

复制代码
using System.Collections.Generic;

namespace SerilogWebTest
{
    public class LogConfig
    {
        public string minlevel { get; set; }
        public Option console { get; set; } = new Option();
        public Option debug { get; set; } = new Option();
        public Option file { get; set; } = new Option();
        public Option elasticsearch { get; set; } = new Option();
        public List<OverrideConfig> overrides { get; set; } = new List<OverrideConfig>();
    }

    public class Option
    {
        public bool enabled { get; set; }
        public List<string> nodes { get; set; } = new List<string>();
        public string indexformat { get; set; }
    }

    public class OverrideConfig
    {
        public string source { get; set; }
        public string minlevel { get; set; }
    }
}
复制代码

定义IHostBuilder扩展类,配置Serilog

using Serilog.Events;
using Serilog.Sinks.Elasticsearch;
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using System.Linq;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace SerilogWebTest
{
    public static partial class Extention
    {
        /// <summary>
        /// 将枚举类型的文本转为枚举类型
        /// </summary>
        /// <typeparam name="TEnum">枚举类型</typeparam>
        /// <param name="enumText">枚举文本</param>
        /// <returns></returns>
        public static TEnum ToEnum<TEnum>(this string enumText) where TEnum : struct
        {
            Enum.TryParse(enumText, out TEnum value);
            return value;
        }
        public static IHostBuilder UseLog(this IHostBuilder hostBuilder)
        {
            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "log.txt");
            return hostBuilder.UseSerilog((hostingContext, serilogConfig) =>
            {
                var envConfig = hostingContext.Configuration;
                LogConfig logConfig = new LogConfig();
                envConfig.GetSection("log").Bind(logConfig);
                logConfig.overrides.ForEach(aOverride =>
                {
                    serilogConfig.MinimumLevel.Override(aOverride.source, aOverride.minlevel.ToEnum<LogEventLevel>());
                });

                serilogConfig.MinimumLevel.Is(logConfig.minlevel.ToEnum<LogEventLevel>());
                if (logConfig.console.enabled)
                    serilogConfig.WriteTo.Console();
                if (logConfig.debug.enabled)
                    serilogConfig.WriteTo.Debug();
                if (logConfig.file.enabled)
                {
                    string template = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3} {SourceContext:l}] {Message:lj}{NewLine}{Exception}";
                    serilogConfig.WriteTo.File(
                        path,
                        outputTemplate: template,
                        rollingInterval: RollingInterval.Day,
                        shared: true,
                        fileSizeLimitBytes: 10 * 1024 * 1024,
                        rollOnFileSizeLimit: true
                        );
                }
                if (logConfig.elasticsearch.enabled)
                {
                    var uris = logConfig.elasticsearch.nodes.Select(x => new Uri(x)).ToList();

                    serilogConfig.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(uris)
                    {
                        IndexFormat = logConfig.elasticsearch.indexformat,
                        AutoRegisterTemplate = true,
                        AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7
                    });
                }
            });
        }
    }

    
}

扩展类中除了允许将日志输出到console、文件,还允许输出到elasticsearch,如果要输出到elasticsearch,需要引入包

.NET .Core 选择日志框架第13张

 在Program中注入日志服务

复制代码
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace SerilogWebTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseLog()//注入服务
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });  
    }
}
复制代码 

免责声明:文章转载自《.NET .Core 选择日志框架》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Android ROM 拆包和打包(压缩虚拟机磁盘的方法)对文件"***.vmdk"操作失败(磁盘空间不足)。磁盘"***.vmdk"所在的文件系统已满。下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

在oracle中 将一个以逗号隔开的String字符串转换成以单引号逗号隔开的集合

在oracle中,使用一条语句实现将'1,2,3'拆分成'1','2','3'的集合将一个以逗号隔开的String字符串转换成以单引号逗号隔开的集合select regexp_substr('1,2,3','[^,]+', 1, level) as para_code1 from dualconnect by regexp_substr('1,2,3','...

EFCore扩展Select方法(根据实体定制查询语句)

EFCore扩展Select方法(根据实体定制查询语句)  通常用操作数据库的时候查询返回的字段是跟 我们的定义的实体是不一致的,所以往往针对UI或者接口层创建大量的Model, 而且需要手动对应字段,非常繁琐。 本文将通过表达式树解决这些重复的过程。  先贴上实现代码   Queryable 类中 的扩展方法  Select<TSource, T...

Spring RedisTemplate操作-ZSet操作(6)

@Autowired @Resource(name="redisTemplate") private RedisTemplate<String, String> rt; public void flushdb(){ rt.execute(new RedisCallback<Obj...

SpringBoot + CXF快速实现SOAP WebService(支持Basic Auth)

唠叨两句 讲真,SOAP跟现在流行的RESTful WebService比起来显得很难用。冗余的XML文本信息太多,可读性差,它的请求信息有时很难手动构造,不太好调试。不过说归说,对某些企业用户来说SOAP的使用率仍然是很高的。 需求背景 接手维护的一个项目,最近客户想开放项目中的功能给第三方调用,而且接入方指定必须是SOAP接口。这项目原来的代码我看着头...

【翻译】Flink Table API &amp;amp; SQL 自定义 Source &amp;amp; Sink

本文翻译自官网:https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/table/sourceSinks.html TableSource 提供访问存储在外部系统(数据库、key-value 存款,消息队列)或文件中的数据的权限。TableSource 在 TableEnviron...

c#读入,写入文本文件

老师留下的作业最后一条题目用到了,就百度下找到了例子修改成两个函数子程序方便使用. //********************************************** //函数名:ReadFile //功能:把指定文本内的数据读出并返回 //参数: FileName文件名 //返...