拥抱.NET Core系列:Logging (1)

在以前咱们简单介绍了 .NET Core 中的 DI组件,没来及了解的童鞋能够翻翻我以前的文章。git

接下来会对 .NET Core 中的 Logging 进行介绍。github

本文中使用了“Microsoft.Extensions.Logging.Console”作为输出目标,后续文章会详解。less

Logging 中的三剑客

image

能够看到 Logging 的核心抽象就是三个接口,分别是:ide

ILogger:负责具体的日志写入逻辑,如:FileLogger,ConsoleLogger,SQLLogger,ElasticsearchLogger 等。单元测试

ILoggerProvider:用来建立记录器,通常和Logger配套使用,至关于单个Logger类型的工厂接口。测试

ILoggerFactory:记录器工厂,直接面向使用者的,使用者能够经过记录器工厂添加记录器提供程序和建立记录器。编码

这几个核心抽象位于 NuGet包:“Microsoft.Extensions.Logging.Abstractions”中。3d

日志等级

在.NET Core提供的日志抽象中提供了7个日志等级(比通常的日志组件多提供了一个Trace和None),分别是:调试

Trace日志

包含最详细消息的日志。 这些消息可能包含敏感的应用程序数据。 默认状况下禁用这些消息,而且不该在生产环境中启用这些消息。

Debug

在开发过程当中用于交互式调查的日志。 这些日志应主要包含对调试有用的信息,不具备长期价值。

Information

跟踪应用程序的通常流程的日志。 这些日志应具备长期价值。

Warning

突出显示应用程序流中异常或意外事件的日志,可是不然不会致使应用程序执行中止。

Error

当当前执行流程因为失败而中止时,会突出显示的日志。这些应该指示当前活动中的故障,而不是应用程序范围的故障。

Critical

描述不可恢复的应用程序或系统崩溃或灾难性的日志失败须要当即关注。

None

不用于写日志消息。 指定记录类别不该写任何消息。

简单的使用

image

image

CreateLogger 方法

CreateLogger 方法的签名为

image

它提供了两个扩展方法,能够经过类型做为分类名称,以下:

image

如何根据类型肯定分类名称?

在扩展方法内部使用了“GetTypeDisplayName(Type type)”来根据类型获取名称(里面有一些逻辑处理,但通常是采用“{命名空间}.{类型名称}”做为分类名称)。

image

实现传送门:https://github.com/aspnet/Logging/blob/patch/1.1.3/src/Microsoft.Extensions.Logging.Abstractions/Internal/TypeNameHelper.cs

Log方法

image

logLevel

日志等级,详情见上文。

eventId(结构体,必填,能够传入 0 或 default(EventId)来充当默认值)

事件ID。

这边的事件ID是用来追踪的,相似 ErrorCode、StatusCode。这样在日志检索的时候能够经过code很方便的找到。

是一个结构体,默认为:“0”。

state(可为null)

状态。

须要记录的对象,这边能够传入任何类型,这就有点奇怪了日志不都是字符吗?

若是我传一个自建类 UserModel 进去会记录出什么信息呢?请接下来看 formatter 参数。

exception(可为null)

异常。

很少说了,若是当前上下文有异常,你丢进去就行了。

formatter(不可为null)

格式化器。

这个参数是一个委托能够看到定义“Func<TState,Exception,string>”,这个就能够解释state是非字符的状况下如何记录日志了。

这边能够经过你本身的逻辑来重建消息的内容(异常信息都会进行输出)。

若是传入null,日志组件会使用默认的格式化器替换,默认的格式化器逻辑是调用“state.ToString()”

扩展方法

固然Logging组件为咱们提供了大量扩展方法以简化咱们的编码。

如下是方法存根,参数说明能够对照上文。

image

EventId效果

image

日志域

image

日志域能够聚合一类的消息,很是适合同一种类型不一样维度的日志记录。

日志过滤器

Logging提供了一个包装实现用来实现日志过滤,咱们先来看看使用。

image

能够看到能够经过制定 CategoryName 及最小日志等级来控制日志是否输出,这边有个有趣的事情。

就是 CategoryName 能够模糊匹配。

在 Logging 组件内部挡识别到 CategoryName 为:“ConsoleApp.MyClass”时会把这个分类名称分割为:

“ConsoleApp.MyClass”

“ConsoleApp”

ps:若是你的命名空间中存在多个“.”符号则还会被分割。

分割完成以后会将这些 Key 拿去与“FilterLoggerSettings”中的字典表进行匹配,优先最大匹配,也就是说若是咱们配置了“ConsoleApp.MyClass”这条项目,则优先使用这条,不然继续寻找“ConsoleApp”这条项目,若是都没匹配到则使用默认的规则。

实现传送门:https://github.com/aspnet/Logging/blob/patch/1.1.3/src/Microsoft.Extensions.Logging.Filter/Internal/FilterLogger.cs

在 NLog、log4jnet 等组件中模糊匹配是采用“.*”的方式,例如:”ConsoleApp.*”,在 .NET Core 中的 Logging 中是不被支持的(把“.*”去掉实现相同的效果),这点须要注意。

注意

“.WithFilter”是使用包装的方式进行集成,因此内部会单独维护一个 FilterLogger,也就意味着全部的 LoggerProvider 必须在 FilterFactory 中进行注册,否则过滤器是不会生效的哦。如下是错误的示例:

image

红色框框部分的两句应该对调,“.WithFilter”应该优先调用。

特殊的Logger => NullLogger

这个我以为 .NET Core 是从 Orchard“偷”过来的,Orchard 满地的 NullLogger.Instance。

为何须要 NullLogger?

在业务系统中,Logger 其实并不影响逻辑,换句话说,Logger若是失败不该该影响业务。

在单元测试时 Logger 也能够忽略。

这句话确定是对的,但在遍地DI的项目中 Logger 颇有可能被开发者传入null,这时候就会影响业务的执行,那么这时候 NullLogger 很是适合作那个最糟糕的实现者。

用来替换日志记录或防止“NullReferenceException”这类异常的发生。

很是惋惜的是,1.1.3版本中没有提供 NullLogger<T> 这样的实现。好消息是在 .NET Standard2.0 中已经提供了 NullLogger<T> 的实现。

咱们下面来看看可以使用的场景:

image

能够看到在没有添加 Logging 组件的时候日志记录也不会抛出异常。

ps:NullLogger<T> 摘抄至.NET Standard2.0中的 NullLoggerOfT.cs。

地址:https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Abstractions/NullLoggerOfT.cs

Logger in DependencyInjection

不使用 Filter

image

使用 Filter

image

写在最后

不得不感叹微软在 .NET Core 中统一了很是多的经常使用组件,为开发者统一环境提供了极大的方便。

后续的文章会分享如何集成第三方 Logging 组件,好比:NLog、log4jnet、Exceptionless 等。

.NET技术栈QQ群:384413261(点击加入 .NET Group

相关文章
相关标签/搜索