面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog。EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用也可以利用提供的API将日志消息写到EventLog中。与EventLog相关的API都定义在System.Diagnostics.EventLog这个类型中,我们不仅仅可以利用它读取、写入和删除日志,还可以使用它来创建和删除Event Source。.NET Core的日志模型利用EventLogLogger实现了与EventLog的集成,不过EventLogLogger使用的是一个抽象化的EventLog。本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、抽象化的EventLog
二、EventLogLogger
三、EventLogLoggerProvider

一、抽象化的EventLog

EventLogLogger定义在“Microsoft.Extensions.Logging.EventLog”这个NuGet包中。就目前这个版本来说,该NuGet包承载的程序集只适用于.NET Framework应用,换句话说,EventLogLogger仅仅提供针对Windows EventLog的支持。尽管如此,日志模型仍然通过一个接口对EventLog的相关操作进行了抽象。

ConsoleLogger采用IConsole接口对针对不同平台的控制台进行了抽象,EventLogLogger使用的抽象化EventLog通过IEventLog接口来表示。如下面的代码片段所示,IEventLog接口仅仅定义了一个唯一的方法WriteEntry来写日志,提供的参数用于指定日志的消息文本(message)、类型(type)、事件ID(eventID)以及类别(category)。为了避免单条日志包含过多的内容,IEventLog接口定义了一个只读属性MaxMessageSize来设置日志消息的文本允许的最大长度。

   1: public interface IEventLog

   2: {

   3:     void WriteEntry(string message, EventLogEntryType type, int eventID, short category);

   4:     int MaxMessageSize { get; }

   5: }

   6:  

   7: public enum EventLogEntryType

   8: {

   9:     Error           = 1, 

  10:     Warning         = 2,

  11:     Information     = 4,

  12:     SuccessAudit    = 8 

  13:     FailureAudit    = 16

  14: }

EventLog记录下来的没条日志都具有一个通过枚举EventLogEntryType表示的类型,这个类型相当于日志的等级。对于定义在这个枚举中的五种类型,Error、Warning和Information与同名的日志等级具有相同的含义,而SuccessAudit和FailureAudit代表针对“审核(Audit)”事件的日志,它们分别代表针对“成功事件”和“失败事件”的审核。当EventLogLogger在利用EventLog写入日志的时候,会将指定的日志等级转化成EventLog的日志类型,转换规则很简单:针对Error、Warning和Information的日志等级转换成同名的EventLog日志类型,其他的等级则之间转换成Information类型。

具有如下定义的 WindowsEventLog是对IEventLog接口的默认实现者。一个WindowsEventLog实际上就是对一个EventLog对象的封装,后者通过DiagnosticsEventLog属性表示。这个封装的EventLog对象在构造函数通过指定相应参数的日志名称(logName)、机器名(machineName)和Event Source名称(sourceName)来创建。在实现的WriteEntry方法中,这个EventLog的WriteEntry被直接调用来完成日志的写入。WindowsEventLog的MaxMessageSize属性返回一个常量31839,日志消息文本不能超过这个长度。

   1: public class WindowsEventLog : IEventLog

   2: {

   3:     private const int MaximumMessageSize = 31839;

   4:  

   5:     public int MaxMessageSize

   6:     {

   7:         get { return MaximumMessageSize; }

   8:     }

   9:  

  10:     public System.Diagnostics.EventLog DiagnosticsEventLog { get; }

  11:  

  12:     public WindowsEventLog(string logName, string machineName, string sourceName)

  13:     {

  14:         DiagnosticsEventLog = new System.Diagnostics.EventLog(logName, machineName, sourceName);

  15:     }

  16:  

  17:     public void WriteEntry(string message, EventLogEntryType type, int eventID, short category)   

  18:     {

  19:         DiagnosticsEventLog.WriteEntry(message, type, eventID, category);

  20:     }

  21: }

二、EventLogLogger

日志模型利用EventLogLogger实现了与EventLog的整合。具体来说,一个EventLogLogger实际上是对EventLog对象的封装,它利用后者向EventLog写入日志。EventLogLogger类型具有如下的定义,我们可以看到它具有两个构造函数,除了提供Logger名称之外,其中一个构造函数具有一个类型为EventLogSettings的参数,这个封装的EventLog对象正式通过该参数提供的信息创建出来的。

   1: public class EventLogLogger : ILogger

   2: {

   3:     public EventLogLogger(string name);

   4:     public EventLogLogger(string name, EventLogSettings settings);

   5:  

   6:     public IDisposable BeginScope<TState>(TState state);  

   7:     public bool IsEnabled(LogLevel logLevel);

   8:     public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, 

   9:     Func<TState, Exception, string> formatter);

  10: }

  11:  

  12: public class EventLogSettings

  13: {

  14:     public IEventLog              EventLog { get; set; }

  15:  

  16:     public string                 LogName { get; set; }

  17:     public string                 MachineName { get; set; }

  18:     public string                 SourceName { get; set; } 

  19:  

  20:     public Func<string, LogLevel, bool>     Filter { get; set; }

  21: }

如上面的代码片段所示,我们可以利用EventLogSettings对象的EventLog属性向EventLogLogger提供一个具体的EventLog对象。如果这个属性为Null,EventLogLogger将会利用EventLogSettings的其他三个属性(LogName、MatcheName和SourceName)创建一个WindowsEventLog对象。除了这四个与创建或者提供EventLog对象相关的四个属性之外,EventLogSettings还具有另一个Func<string, LogLevel, bool>类型的属性Filter,它自然代表日志消息过滤器。

当EventLogLogger在利用EventLogSettings创建WindowsEventLog对象的时候,如果相关的属性(LogName、MatcheName和SourceName)在EventLogSettings对象中未作显式设置,它会采用预定义的默认值。具体来说这三者对应的默认值为“Application”[1]、“.”(代表本机)和“Application”。如果我们调用第一个构造函数重载,实际上内部会利用这些默认值创建一个WindowsEventLog对象。如果调用构造函数时连名称(name参数)都没有指定,那么类型名称(“EventLogLogger”)将被用来命名创建的Logger。

EventLogLogger和DebugLogger一样也不支持日志上下文范围,所以它的BeginScope<TState>方法和返回的对象其实毫无意义。EventLogSettings的Filter属性返回Func<string, LogLevel, bool>对象将被IsEnabled方法使用,如果个委托对象没有被显式提供,意味着这个方法总是返回True。当Log方法被调用的时候,它会采用与DebugLogger完全一致的方式来格式化最终的日志消息文本,所以针对异常的重复描述的问题依然存在。日志消息最终通过调用EventLog的WriteEntry方法被写到EventLog中,但是在这之前会检查格式化后的日志消息文本是否超过通过MaxMessageSize属性限制的长度,如果超过这个限制,日志消息将会被拆分并分多次写入EventLog。

三、EventLogLoggerProvider

EventLogLogger由对应的EventLogLoggerProvider来提供,下面的代码体现了这个类型完整的定义。我们可以调用如下所示的三个扩展方法AddEventLog来创建相应的EventLogLoggerProvider并将其注册到指定的LoggerFactory之上,我们可以通过这个方法指定用于提供或者辅助创建EventLog的EventLogSettings对象和最低日志等级。

   1: public class EventLogLoggerProvider : ILoggerProvider, IDisposable

   2: {

   3:     private readonly EventLogSettings _settings;

   4:     public EventLogLoggerProvider() : this(null)

   5:     {}

   6:  

   7:     public EventLogLoggerProvider(EventLogSettings settings)

   8:     {

   9:         _settings = settings;

  10:     }

  11:  

  12:     public ILogger CreateLogger(string name)

  13:     {

  14:         return new EventLogLogger(name, _settings ?? new EventLogSettings());

  15:     }

  16:  

  17:     public void Dispose()

  18:     {}

  19: }

  20:  

  21: public static class EventLoggerFactoryExtensions

  22: {

  23:     public static ILoggerFactory AddEventLog(this ILoggerFactory factory);

  24:     public static ILoggerFactory AddEventLog(this ILoggerFactory factory, EventLogSettings settings);

  25:     public static ILoggerFactory AddEventLog(this ILoggerFactory factory, LogLevel minLevel);

  26: }

我们同样通过一个简单的控制台应用来演示针对EventLog的日志记录。我们首选创建一个空的控制台应用,并在project.json中添加所需的依赖。由于针对EventLog的日志记录只适用于.NET Framework应用,所以我们仅仅为应用定义了一个针对.NET Framework 4.6.1(net461)的框架。

   1: {

   2:   ...

   3:   "dependencies": {

   4:     "Microsoft.Extensions.DependencyInjection"    : "1.0.0",

   5:     "Microsoft.Extensions.Logging"                : "1.0.0",

   6:     "Microsoft.Extensions.Logging.EventLog"       : "1.0.0"

   7:   },

   8:  

   9:    "frameworks": {

  10:     "net461": {

  11:       "frameworkAssemblies": {

  12:         "System.Runtime": {

  13:           "type": "build"

  14:         }

  15:       }

  16:     }

  17:   }

  18: }

我们编写了下面这段程序来完成针对EventLog的日志记录。如下面的代码片段所示,我们首先为即将写入的日志创建了一个名为“Demo”的Event Source(它一般代表日志被写入的应用或者服务的名称)。接下来我们采用依赖注入的方式创建了一个LoggerFactory对象,并调用扩展方法AddEventLog创建了一个EventLoggerProvider对象并将其注册到LoggerFactory上。我们在调用这个AddEventLog方法时指定了一个EventLogSettings对象,并将其SourceName属性设置为“Demo”。我们最终利用这个LoggerFactory对象创建出对应的Logger,并利用它写入了一条等级为Error的日志。

   1: public class Program

   2: {

   3:     public static void Main(string[] args)

   4:     {

   5:         if(!EventLog.SourceExists("Demo"))

   6:         {

   7:             EventLog.CreateEventSource("Demo", "Application");

   8:         }

   9:  

  10:         new ServiceCollection()

  11:             .AddLogging()

  12:             .BuildServiceProvider()

  13:             .GetService<ILoggerFactory>()

  14:             .AddEventLog(new EventLogSettings { SourceName = "Demo" })

  15:             .CreateLogger<Program>()

  16:             .LogError("数据库连接失败(数据库:{ Database},用户名:{ User})", "TestDb", "sa");

  17:     }

  18: }

由于上面这段程序涉及到Event Source查看和创建,所以需要在管理员权限下才能正常运行。程序运行后查看Event Viewer,我们将会看到被写入的这条日志消息。如图10所示,由于我们调用扩展方法AddEventLog时提供的EventLogSettings并没有显式指定EventLog名称,所以我们的日志默认会写入Application这个EventLog。


[1] Windows默认提供了三种EventLog,分别是Application、System和Security,应用产生的日志只能写到Application和System日志中,Security日志是只读的。除了这三种EventLog,我们还可以为应用创建独立的EventLog。

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

.NET Core的日志[4]:将日志写入EventLog的更多相关文章

  1. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

  2. .NET Core的日志[2]:将日志输出到控制台

    对于一个控制台应用,比如采用控制台应用作为宿主的ASP.NET Core应用,我们可以将记录的日志直接输出到控制台上.针对控制台的Logger是一个类型为ConsoleLogger的对象,Consol ...

  3. 将日志写入EventLog

    将日志写入EventLog 面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针 ...

  4. Net Core平台灵活简单的日志记录框架NLog+SqlServer初体验

    Net Core平台灵活简单的日志记录框架NLog+SqlServer初体验 前几天分享的"[Net Core平台灵活简单的日志记录框架NLog+Mysql组合初体验][http://www ...

  5. Net Core平台灵活简单的日志记录框架NLog+Mysql组合初体验

    Net Core平台灵活简单的日志记录框架NLog初体验 前几天分享的"[Net Core集成Exceptionless分布式日志功能以及全局异常过滤][https://www.cnblog ...

  6. .net core使用ocelot---第三篇 日志记录

    简介 .net core使用ocelot---第一篇 简单使用 .net core使用ocelot---第二篇 身份验证使用 上篇介绍使用asp.net core 创建API网关.本文将介绍Ocelo ...

  7. .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行)

    .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行) 遇到个诡异的问题,项目发布并寄宿到 IIS上后,Log4Net没有日志输出 1.原因分析 这不应该啊,所有的配置 ...

  8. .NET Core + K8S + Loki 玩转日志聚合

    1. Intro 最近在了解日志聚合系统,正好前几天看到一篇文章<用了日志系统新贵Loki,ELK突然不香了!>,所以就决定动手体验一下.本文就带大家快速了解下Loki,并简单介绍.NET ...

  9. .net core 中使用Log4net输出日志到Mysql数据库中

    .net core 中使用Log4net输出日志到数据库中去 1.使用Nuget安装log4net 和 mysql.data 2.设置log4net 的配置文件 log4net.config 可以设置 ...

随机推荐

  1. 使用TSQL查询和更新 JSON 数据

    JSON是一个非常流行的,用于数据交换的文本数据(textual data)格式,主要用于Web和移动应用程序中.JSON 使用“键/值对”(Key:Value pair)存储数据,能够表示嵌套键值对 ...

  2. JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome

    今天为大家分享一下我自己制作的浏览器滚动条,我们知道用css来自定义滚动条也是挺好的方式,css虽然能够改变chrome浏览器的滚动条样式可以自定义,css也能够改变IE浏览器滚动条的颜色.但是css ...

  3. JS 判断数据类型的三种方法

    说到数据类型,我们先理一下JavaScript中常见的几种数据类型: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Functi ...

  4. Visual Studio 2012远程调试中遇到的问题

    有的时候开发环境没问题的代码在生产环境中会某些开发环境无法重现的问题,或者需要对生产环境代码进行远程调试该怎么办? Vs已经提供给开发者远程调试的工具 下面简单讲讲该怎么用,前期准备:1.本地登录账户 ...

  5. CSS垂直居中的11种实现方式

    今天是邓呆呆球衣退役的日子,在这个颇具纪念意义的日子里我写下自己的第一篇博客,还望前辈们多多提携,多多指教! 接下来,就进入正文,来说说关于垂直居中的事.(以下这11种垂直居中的实现方式均为笔者在日常 ...

  6. Opserver开源的服务器监控系统(ASP.NET)

    Opserver是Stack Exchange下的一个开源监控系统,系统本身由C#语言开发的ASP.NET(MVC)应用程序,无需任何复杂的应用配置,入门很快.下载地址:https://github. ...

  7. js参数arguments的理解

    原文地址:js参数arguments的理解 对于函数的参数而言,如下例子 function say(name, msg){ alert(name + 'say' + msg); } say('xiao ...

  8. python 数据类型 -- 元组

    元组其实是一种只读列表, 不能增,改, 只可以查询 对于不可变的信息将使用元组:例如数据连接配置 元组的两个方法: index, count >>> r = (1,1,2,3) &g ...

  9. Three.JS 从世界坐标系转换到屏幕坐标系

    var projector = new THREE.Projector(); var world_vector = new THREE.Vector3(0,0,1); var vector = pro ...

  10. Jquery 获得当前标签的名称和标签属性

    得到标签的名称 $("#name").prop("tagName"); 或者 $("#name")[0].tagName; 注意:1.得到的 ...