先来介绍.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组件,类库结构如下:
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类库,结构如下:
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(); } } }
效果如下:
3、Netcore中使用log4net
新建一个netcore mvc项目,通过nuget引入包Microsoft.Extensions.Logging.Log4Net.AspNetCore,项目结构如下:
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 }); } } }
效果如下:
二、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,将该文件属性改为始终复制。项目结构如下:
修改下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 }); } } }
效果如下:
三、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包
新建一个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,需要引入包
在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>(); }); } }