.NET Core的日志[5]:利用TraceSource写日志

从微软推出第一个版本的.NET
Framework的时候,就在“System.Diagnostics”命名空间中提供了Debug和Trace两个类帮助我们完成针对调试和跟踪信息的日志记录。在.NET
Framework 2.0中,微软引入了TraceSource并对跟踪日志系统进行了优化,优化后的跟踪日志系统在.NET
Core中又经过了相应的简化。.NET
Core的日志模型借助TraceSourceLoggerProvider实现对TraceSource的整合,在正式介绍这个Logger之前,我们先来认识一下TraceSource跟踪日志系统中的三个核心对象。[
本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、基于TraceSource的追踪日志系统
二、TraceSourceLogger
三、TraceSourceLoggerProvider

一、基于TraceSource的追踪日志系统

对于这个基于TraceSource的跟踪日志系统来说,除了TraceSource之外,它还具有额外连个核心的对象,它们分别是TraceListener和SourceSwitch,三者之间的关系如下图所示。日志消息的写入实现在TraceListener上,我们可以将一组TraceListener注册到某个TraceSource之上。当我们利用TraceSource记录某条跟踪日志时,日志消息会分发给注册的每一个TraceListener并由它们将日志消息写到对应的目的地。每个TraceSource都具有一个SourceSwitch,后者起到了日志过滤的作用。具体来说,SourceSwitch定义了相应的过滤条件来帮助TraceSource决定是否应该将跟踪日志分发给TraceListener,如果指定的日志消息不满足过滤条件,TraceSource将不会进行任何实质性的日志记录工作。

如下所示的是TraceSource的定义。每一个TraceSource都具有一个名称,它一般代表写入跟踪日志的应用程序、服务或者组件的名称。我们可以调用它的三组Trace方法(TraceData、TraceEvent和TraceInformation)来记录跟踪日志。由于这些方法都标注了一个ConditionaleAttribute特性并将条件编译符“TRACE”,所以针对这些方法的调用只有在针对Trace模式编译的应用中才是有效的。

   1: public class TraceSource
   2: {
   3:     public TraceListenerCollection Listeners { get; }
   4:     public string             Name { get; }
   5:     public SourceSwitch         Switch { get; set; }
   6:  
   7:     public TraceSource(string name);
   8:     public TraceSource(string name, SourceLevels defaultLevel);
   9:    
  10:     [Conditional("TRACE")]
  11:     public void TraceData(TraceEventType eventType, int id, object data);
  12:     [Conditional("TRACE")]
  13:     public void TraceData(TraceEventType eventType, int id, params object[] data);
  14:  
  15:     [Conditional("TRACE")]
  16:     public void TraceEvent(TraceEventType eventType, int id);
  17:     [Conditional("TRACE")]
  18:     public void TraceEvent(TraceEventType eventType, int id, string message);
  19:     [Conditional("TRACE")]
  20:      public void TraceEvent(TraceEventType eventType, int id, string format, params object[] args);
  21:  
  22:     [Conditional("TRACE")]
  23:     public void TraceInformation(string message);
  24:     [Conditional("TRACE")]
  25:     public void TraceInformation(string format, params object[] args);  
  26: }

通过TraceData、TraceEvent和TraceInformation这三个方法记录的跟踪日志都具有一个通过枚举类型TraceEventType表示的事件类型,它相当于前面提到的日志等级。TraceEventType的这些枚举项的值越小意味着等级越高,定义日志等级的LogLevel则于此相反。在调用TraceData和TraceEvent方法时,我们需要显式地为写入的跟踪日志指定事件类型,而TraceInformation方法则默认使用Information类型。

   1: public enum TraceEventType
   2: {
   3:     Critical         = 1,
   4:     Error            = 2,
   5:     Warning          = 4,
   6:     Information      = 8,
   7:     Verbose          = 16,
   8: }

与TraceEventType枚举对应的还具有另一个名为SourceLevels的枚举,除了包含五种具体事件类型之外,还具有额外两个选项All和Off,该枚举对象被SourceSwitch用来过滤日志。在调用构造函数创建TraceSource的时候,我们可以指定一个SourceLevels枚举值作为默认的等级。如果这个等级未作显式设置,创建的TraceSource采用的等级为Off,这意味着默认情况下针对追踪日志的记录是禁止的。

   1: [Flags]
   2: public enum SourceLevels
   3: {
   4:     All             = -1,
   5:     Off             = 0,
   6:     Critical        = 1,
   7:     Error           = 3,
   8:     Warning         = 7
   9:     Information     = 15,
  10:     Verbose         = 31
  11: }

我们创建的TraceSource是指定(或者默认设置)的表示日志等级的SourceLevels枚举会用来创建一个具有如下定义的SourceSwitch对象,TraceSource的Switch属性返回的就是这么一个对象。顾名思义,SourceSwitch是一个开关,它利用ShouldTrace方法决定了针对某种类型的跟踪日志的写入操作是应该开启还是关闭。如下面的代码片段所示,ShouldTrace方法返回的结果是根据通过Level属性返回的跟踪日志等级计算出来的,表示跟踪日志等级的SourceLevels枚举正是最初正是由TraceSource在初始化时提供的。

   1: public class SourceSwitch : Switch
   2: {
   3:     public SourceLevels Level {get;set;}
   4:  
   5:     public SourceSwitch(string name);
   6:     public SourceSwitch(string displayName, string defaultSwitchValue);
   7:  
   8:     public bool ShouldTrace(TraceEventType eventType)
   9:     {
  10:         return ((base.SwitchSetting & eventType) > 0);
  11:     }    
  12: }

TraceSource对象自身并不负责针对跟踪日志的写入,它仅仅将日志的写入请求分发给注册的TraceListener并委托它们来完成写日志的功能。这些注册到TraceSource上的TraceListenter被保存到由它的Listeners属性返回的集合对象中。所有的TraceListener都拍生于如下这个抽象的TraceListener类型,它定义了如下两组TraceData和TraceEvent方法。当我们调用TraceSource的TraceData、TraceEvent和TraceInformation方法时,如果通过SourceSwitch判断应该开启针对当前跟踪日志的写入功能,那么注册的TraceListener的TraceData或者TraceEvent方法将会被调用。

   1: public abstract class TraceListener : IDisposable
   2: {
   3:     ...
   4:     public virtual void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data);
   5:     public virtual void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data);
   6:  
   7:     public virtual void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id);
   8:     public virtual void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message);
   9:     public virtual void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args);
  10: }

接下来我们通过一个简单的控制台应用来演示如何创建一个TraceSource并使用它来记录追踪日志。由于TraceSource定义在“System.Diagnostics.TraceSource”这个NuGet包中,我们需要在project.json文件中需要按照如下的方式添加针对这个NuGet包的依赖。和前面演示的实例一样,为了提供针对中文编码的支持,我们不得不添加针对“System.Text.Encoding.CodePages”这个NuGet包的依赖。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "System.Diagnostics.TraceSource": "4.0.0",    
   5:     "System.Text.Encoding.CodePages": "4.0.1"
   6:   }
   7: }

由于TraceSource总是利用注册在它上面的TraceListener来完成写日志的工作,所以我们按照如下的方式自定义了ConsoleTraceListener。顾名思义,ConsoleTraceListener旨在将分发给它的追踪日志输出到控制台上。如下面的代码片段所示,这个ConsoleTraceListener仅仅重写了Write和WriteLine方法,它们调用定义在Console类型上的同名方法将格式化好的日志消息输出到控制台上。

   1: public class ConsoleTraceListener : TraceListener
   2: {
   3:     public override void Write(string message) => Console.Write(message);
   4:     public override void WriteLine(string message) => Console.WriteLine(message);
   5: }

我们在作为程序入口的Main方法中创建了一个TraceSource对象。在调用构造函数的时候,除了指定TraceSource的名称(“Program”)之外,我们还设置了一个默认的追踪日志等级(Warning)。接下来我们创建了一个ConsoleTraceListener对象并将其注册到TraceSource对象上。在此之后,我们调用TraceSource的TraceEvent方法记录了三条追踪日志,它们采用的追踪事件类型分别是Information、Warining和Error。

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         //注册EncodingProvider实现对中文编码的支持
   6:         Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
   7:  
   8:         TraceSource traceSource = new TraceSource(nameof(Program), SourceLevels.Warning);
   9:         traceSource.Listeners.Add(new ConsoleTraceListener());
  10:  
  11:         int eventId = 3721;
  12:         traceSource.TraceEvent(TraceEventType.Information, eventId, "升级到最新.NET Core版本({0})", "1.0.0");
  13:         traceSource.TraceEvent(TraceEventType.Warning, eventId, "并发量接近上限({0}) ", 200);
  14:         traceSource.TraceEvent(TraceEventType.Error, eventId, "数据库连接失败(数据库:{0},用户名:{1})", "TestDb", "sa");
  15:     }
  16: }

该程序运行之后,我们利用TraceSource记录的追踪日志将会被注册的ConsoleTraceListener按照如下图所示的形式输出到控制台上。由于我们在创建TraceSource的时候指定了一个默认的追踪日志等级Warning,所以只有不低于这个等级的两条日志才会显示在控制台上。

二、TraceSourceLogger

.NET
Core的日志模型利用一个定义在NuGet包“Microsoft.Extensions.Logging.TraceSource”中的TraceSourceLogger类型实现与TraceSource跟踪日志系统的整合。从如下面的代码片段我们不难看出,一个TraceSourceLogger对象实际上就是对一个TraceSource对象的封装,在实现的Log<State>方法中,它会调用TraceSource的TraceEvent方法来完成针对日志消息的写入工作。

   1: public class TraceSourceLogger : ILogger
   2: {
   3:     public TraceSourceLogger(TraceSource traceSource);
   4:     public IDisposable BeginScope<TState>(TState state);
   5:     public bool IsEnabled(LogLevel logLevel);
   6:     public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
   7: }

当我们调用TraceSource的TraceEvent方法来写追踪日志的时候,需要指定追踪日志的事件类型,该类型由提供的日志等级来决定,下表展示了日志等级与跟踪事件类型之间的映射关系很简单。

由于TraceSource通过调用其SourceSwitch的ShouldTrace方法来决定是否真正需要写入当前分发的追踪日志消息,所以当TraceSourceLogger的IsEnabled方法被调用的时候,它也会按照这样的映射关系将指定的日志等级转换成追踪事件类型,并将其作为参数调用这个ShouldTrace方法,这个方法的返回值就是IsEnabled方法的返回值。


日志等级


跟踪事件类型


Trace


Verbose


Debug


Verbose


Information


Information


Warning


Warning


Error


Error


Critical


Critical

TraceSourceLogger的BeginScope<TState>方法会返回一个TraceSourceScope对象,虽然这是一个共有的类型,但是这个对象并不做任何作用域的控制,其自身也不携带任何关于当前日志上下文的信息,所以TraceSourceLogger和前面介绍的DebugLogger和EventLogLogger一样,其实都不提供针对日志上下文的支持。

三、TraceSourceLoggerProvider

TraceSourceLogger对应的LoggerProvider类型为TraceSourceLoggerProvider。如下面的代码片段所示,当我们创建一个TraceSourceLoggerProvider对象时需要提供一个SourceSwitch和TraceListener对象(可选)。在实现的CreateLogger方法中,TraceSourceLoggerProvider会根据指定的名称创建一个TraceSource对象,它将采用初始化时指定的SourceSwitch,预先指定的TraceListener也会注册到这个TraceSource对象上,CreateLogger方法最终返回的将是根据这个TraceSource创建的TraceSourceLogger。

   1: public class TraceSourceLoggerProvider : ILoggerProvider
   2: {   
   3:     public TraceSourceLoggerProvider(SourceSwitch rootSourceSwitch);
   4:     public TraceSourceLoggerProvider(SourceSwitch rootSourceSwitch, TraceListener rootTraceListener);
   5:  
   6:     public ILogger CreateLogger(string name);
   7:     public void Dispose();   
   8: }

值得一提的是TraceSourceLoggerProvider并不会在CreateLogger方法中频繁地创建TraceSource对象,而是选择将创建的TraceSource会根据指定的名称被缓存起来。所以当CreateLogger方法被调用的时候,TraceSourceLoggerProvider会根据指定的名称查看缓存中是否存在一个现成的TraceSource,如果存在则直接根据它创建返回的TraceSourceLogger。只有在确定同名的TraceSource不曾创建的情况下,新的TraceSource才会被真正创建出来。我们可以调用如下两个扩展方法AddTraceSource根据指定的SourceSwitch(或者它的名称)和TraceListener来创建TraceSourceLoggerProvider并将其注册到指定的LoggerFactory上。

   1: public static class TraceSourceFactoryExtensions
   2: {
   3:     public static ILoggerFactory AddTraceSource(this ILoggerFactory factory, SourceSwitch sourceSwitch, TraceListener listener);
   4:     public static ILoggerFactory AddTraceSource(this ILoggerFactory factory, string switchName, TraceListener listener);
   5: }

接下来我们通过一个简单的实例来演示针对DebugLogger的日志记录。我们创建一个空的控制台应用,在添加必要的依赖之后,我们在Main方法中编写了如下一段程序。如下面的代码片段所示,我们采用依赖注入的方式创建了一个LoggerFactory,并调用扩展方法AddTraceSource方法创建并注册了一个TraceSourceLoggerProvider对象。在利用LoggerFactory创建出Logger对象之后,我们利用后者记录了三条日志消息。

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         //注册EncodingProvider实现对中文编码的支持
   6:         Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
   7:  
   8:         ILogger logger = new ServiceCollection()
   9:                 .AddLogging()
  10:                 .BuildServiceProvider()
  11:                 .GetService<ILoggerFactory>()
  12:                 .AddTraceSource(new SourceSwitch(nameof(Program), "Warning"), new ConsoleTraceListener())
  13:                 .CreateLogger<Program>();
  14:  
  15:  
  16:         int eventId = 3721;
  17:  
  18:         logger.LogInformation(eventId, "升级到最新.NET Core版本({version})", "1.0.0");
  19:         logger.LogWarning(eventId, "并发量接近上限({maximum}) ", 200);
  20:         logger.LogError(eventId, "数据库连接失败(数据库:{Database},用户名:{User})", "TestDb", "sa");
  21:     }
  22: }

我们在调用扩展方法AddTraceSource创建并注册TraceSourceLoggerProvider是指定了一个针对Warning等级的SourceSwitch,而指定的TraceListener是一个自定义的ConsoleTraceListener,所以只有两条等级不低于Warning的日志消息会被这个ConsoleTraceListener按照上图所示的形式输出到控制台上。



.NET Core的日志[1]:采用统一的模式记录日志
.NET Core的日志[2]:将日志写入控制台
.NET Core的日志[3]:将日志写入Debug窗口
.NET Core的日志[4]:利用EventLog写日志
.NET Core的日志[5]:利用TraceSource写日志

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-09-20 17:43:54

.NET Core的日志[5]:利用TraceSource写日志的相关文章

手机QQ可以写日志吗?手机QQ写日志的教程

大家最好是把你的手机QQ升级到最新版本,否则有些版本界面是不会一样特别是老版本估计没这个功能. 1.先登录手机QQ,找到界面下的[动态]打开 2.然后在进入动态界面点击[好友动态] 3.现在就进入到你手机QQ空间动态,点击右上角的[+]按钮,如图图所示 4.你会发现没有看到那里有[写日志]入口了,怎么办? 5.可见手机QQ是没有[写日志]所以我们要下载一个"手机QQ空间" 6.然后我们打开手机QQ空间点击[我的空间]打开进入 7.然后你会看到有一个[日志]选项了,我们点击它 8.然后在

写日志的那些事儿

写日志简介 一般提到写日志,主要有下面几种不同的场景: 诊断日志:应用打印异常信息,排查问题用,一般是给人看的,输出格式会比较随意,里面可能会有异常堆栈,或者排查问题用的一些文本信息: 数据日志:一般是用来做监控和数据分析的,可以人肉临时分析,也可以给机器分析,要求格式比较固定: 交易日志:一般在日志式文件系统.NoSQL.DB 中使用,一般有 journaling,WAL(write-ahead logging),binlog.这种日志通常都不是给人看的. EagleEye 写的日志,是数据日

C++语言编写写日志类_C 语言

使用C++语言编写写日志类,支持写日志级别设置.支持多线程.支持可变形参表写日志. 主要提供以下接口: 1.设置写日志的级别 2.写关键日志信息 3.写错误日志信息 4.写警告日志信息 5.写一般日志信息 #ifndef COMMAND_DEFINE_H #define COMMAND_DEFINE_H //日志级别的提示信息 static const char * KEYINFOPREFIX = " Key: \n"; static const char * ERRORPREFIX

ASP.NET Core应用中如何记录和查看日志

日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.LoggerFactory和LoggerProvider这三个核心对象组成.我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加. [ 本文已经同步到<ASP.NET Core框架揭秘>之中] 目录 一. 配置LoggerFactory 二.以当前请求作为日志范

详解ASP.NET Core应用中如何记录和查看日志_实用技巧

日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.LoggerFactory和LoggerProvider这三个核心对象组成.我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加. 一. 配置LoggerFactory 我们在上面一节演示了一个展示ASP.NET Core默认注册服务的实例,细心的读者一定会看到显

.NET Core下的日志(2):日志模型详解

NET Core的日志模型主要由三个核心对象构成,它们分别是Logger.LoggerProvider和LoggerFactory.总的来说,LoggerProvider提供一个具体的Logger对象将格式化的日志消息写入相应的目的地,但是我们在编程过程中使用的Logger对象则由LoggerFactory创建,这个Logger利用注册到LoggerFactory的LoggerProvider来提供真正具有日志写入功能的Logger,并委托后者来记录日志. 目录 一.Logger     扩展方

国人成亚历山大帝 睡不好可写日志助眠

近日,世界知名的办公方案提供商雷格斯发布了一项最新调查结果:http://www.aliyun.com/zixun/aggregation/12536.html">中国内地上班族在过去一年内所承受的压力,位列全球第一.压力之下必有夜猫,入睡困难.多梦.睡眠浅等成为越来越多都市人的睡眠障碍,连诺贝尔文学奖得主莫言的妻子也透露莫老常常因写作而睡不着觉. 但互联网时代的睡眠问题起因可不是那么单纯,"关上电脑去睡觉,躺在床上玩手机"的生活模式造就了一大批城市夜猫子.如何改善睡眠

谈谈如何利用校内网日志进行网站推广

校内网刚建立的时候一个最重要的特点是限制具有特定大学IP地址或者大学电子邮箱的用户注册,这样就保证了注册用户绝大多数都是在校大学生.用户注册之后可以粘贴自己的照片,撰写日志,签写留言等.该网站鼓励大学生用户实名注册,上传真实照片,让大学生在网络上体验到现实生活的乐趣. 经过将近三年的快速发展校内网已经发展成为为整个中国互联网用户提供服务的SNS社交网站,给不同身份的人提供了一个全方位的互动交流平台,大大提高了用户之间的交流效率降低了维护用户之间交流的成本,通过提供发布日志.保存相册.音乐视频等站

kettle作业中的js如何写日志文件

在kettle作业中JavaScript脚本有时候也扮演非常重要的角色,此时我们希望有一些日志记录.下面是job中JavaScript记录日志的方式. job的js写日志的方法. 得到日志输出实例 org.pentaho.di.core.logging.LogWriter.getInstance(); 按照日志的级别输出: public void logMinimal(String subject, String message, Object... args) { println(LOG_LE