拥抱.NET Core系列:Logging (1)
在之前我们简单介绍了 .NET Core 中的 DI组件,没来及了解的童鞋可以翻翻我之前的文章。
接下来会对 .NET Core 中的 Logging 进行介绍。
本文中使用了“Microsoft.Extensions.Logging.Console”做为输出目标,后续文章会详解。
Logging 中的三剑客
可以看到 Logging 的核心抽象就是三个接口,分别是:
ILogger:负责具体的日志写入逻辑,如:FileLogger,ConsoleLogger,SQLLogger,ElasticsearchLogger 等。
ILoggerProvider:用来创建记录器,一般和Logger配套使用,相当于单个Logger类型的工厂接口。
ILoggerFactory:记录器工厂,直接面向使用者的,使用者可以通过记录器工厂添加记录器提供程序和创建记录器。
这几个核心抽象位于 NuGet包:“Microsoft.Extensions.Logging.Abstractions”中。
日志等级
在.NET Core提供的日志抽象中提供了7个日志等级(比一般的日志组件多提供了一个Trace和None),分别是:
Trace
包含最详细消息的日志。 这些消息可能包含敏感的应用程序数据。 默认情况下禁用这些消息,并且不应在生产环境中启用这些消息。
Debug
在开发过程中用于交互式调查的日志。 这些日志应主要包含对调试有用的信息,不具有长期价值。
Information
跟踪应用程序的一般流程的日志。 这些日志应具有长期价值。
Warning
突出显示应用程序流中异常或意外事件的日志,但是否则不会导致应用程序执行停止。
Error
当当前执行流程由于失败而停止时,会突出显示的日志。这些应该指示当前活动中的故障,而不是应用程序范围的故障。
Critical
描述不可恢复的应用程序或系统崩溃或灾难性的日志失败需要立即关注。
None
不用于写日志消息。 指定记录类别不应写任何消息。
简单的使用
CreateLogger 方法
CreateLogger 方法的签名为
它提供了两个扩展方法,可以通过类型作为分类名称,如下:
如何根据类型确定分类名称?
在扩展方法内部使用了“GetTypeDisplayName(Type type)”来根据类型获取名称(里面有一些逻辑处理,但一般是采用“{命名空间}.{类型名称}”作为分类名称)。
Log方法
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组件为我们提供了大量扩展方法以简化我们的编码。
以下是方法存根,参数说明可以对照上文。
EventId效果
日志域
日志域可以聚合一类的消息,非常适合同一种类型不同维度的日志记录。
日志过滤器
Logging提供了一个包装实现用来实现日志过滤,我们先来看看使用。
可以看到可以通过制定 CategoryName 及最小日志等级来控制日志是否输出,这边有个有趣的事情。
就是 CategoryName 可以模糊匹配。
在 Logging 组件内部挡识别到 CategoryName 为:“ConsoleApp.MyClass”时会把这个分类名称分割为:
“ConsoleApp.MyClass”
“ConsoleApp”
ps:如果你的命名空间中存在多个“.”符号则还会被分割。
分割完成之后会将这些 Key 拿去与“FilterLoggerSettings”中的字典表进行匹配,优先最大匹配,也就是说如果我们配置了“ConsoleApp.MyClass”这条项目,则优先使用这条,否则继续寻找“ConsoleApp”这条项目,如果都没匹配到则使用默认的规则。
在 NLog、log4jnet 等组件中模糊匹配是采用“.*”的方式,例如:”ConsoleApp.*”,在 .NET Core 中的 Logging 中是不被支持的(把“.*”去掉实现相同的效果),这点需要注意。
注意
“.WithFilter”是使用包装的方式进行集成,所以内部会单独维护一个 FilterLogger,也就意味着所有的 LoggerProvider 必须在 FilterFactory 中进行注册,不然过滤器是不会生效的哦。以下是错误的示例:
红色框框部分的两句应该对调,“.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> 的实现。
我们下面来看看可使用的场景:
可以看到在没有添加 Logging 组件的时候日志记录也不会抛出异常。
ps:NullLogger<T> 摘抄至.NET Standard2.0中的 NullLoggerOfT.cs。
Logger in DependencyInjection
不使用 Filter
使用 Filter
写在最后
不得不感叹微软在 .NET Core 中统一了非常多的常用组件,为开发者统一环境提供了极大的方便。
后续的文章会分享如何集成第三方 Logging 组件,比如:NLog、log4jnet、Exceptionless 等。
.NET技术栈QQ群:384413261(点击加入 .NET Group)
拥抱.NET Core系列:Logging (1)的更多相关文章
- 拥抱.NET Core系列:依赖注入(2)
上一篇"拥抱.NET Core系列:依赖注入(1)"大体介绍了服务注册.获取和生命周期,这一篇来做一些补充. 由于内容跨度大(.NET Core.ASP.NET Core),所以文 ...
- 拥抱.NET Core系列:MemoryCache 缓存过期
在上一篇"拥抱.NET Core系列:MemoryCache 初识"中我们基本了解了缓存的添加.删除.获取,那么今天我们来看看缓存的过期机制.这里和上篇一样将把"Micr ...
- 拥抱.NET Core系列:MemoryCache 缓存选项
在上一篇 "拥抱.NET Core系列:MemoryCache 缓存过期" 中我们详细的了解了缓存过期相关的内容,今天我们来介绍一下 MSCache 中的 Options,由此来介 ...
- 拥抱.NET Core系列:MemoryCache 缓存域
在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MSCache 中的缓存域. MSCache项目 M ...
- 拥抱.NET Core系列:MemoryCache 缓存域(转载)
阅读目录 MSCache项目 缓存域 写在最后 在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MS ...
- 拥抱.NET Core系列:MemoryCache 缓存选项 (转载)
阅读目录 MSCache项目 MemoryCacheOptions ExpirationScanFrequency SizeLimit CompactionPercentage 写在最后 在上一篇 ” ...
- 拥抱.NET Core系列:MemoryCache 缓存过期 (转载)
阅读目录 MSCache项目 MSCache提供的过期方式 绝对时间到期 滑动时间到期 自定义过期策略 过期策略组合拳 缓存过期回调 写在最后 在上一篇”拥抱.NET Core系列:MemoryCac ...
- 拥抱.NET Core系列:依赖注入(1)
依赖注入时编程手段中解耦和封装的一个非常重要的手段,我本人已经到了没有DI无法编写项目的程度了,在.NET Framework中微软并没有在FCL中引入DI,虽然推出了"Unity" ...
- 拥抱.NET Core系列:MemoryCache 初识
Cache是一个绝大多数项目会用到的一个技术,说起到缓存可能就联想到 Set.Add.Get.Remove.Clear 这几个方法.那么在.NET Core中微软给我们带来了什么样的缓存体验呢?今天我 ...
随机推荐
- 使用cnpm搭建私有NPM仓库 发布npm包
关于如何使用cnpm搭建私有的npm仓库看这里→ http://blog.fens.me/nodejs-cnpm-npm/ 我本人还没有机会真正实践操作过,公司的npm仓库是我老大搭建的,我这里仅仅记 ...
- python基础 --02
常见的数据类型 列表 在python中,列表的创建可以是由[]两个方括号组成的.在其他语言中,被称之为数组. 列表里可以存放一组值,并且系统默认的给列表里的每一个元素以索引值,方便查找和使用. 如下: ...
- linux新学篇
[学会使用快捷键] Ctrl + C:这个是用来终止当前命令的快捷键,当然你也可以输入一大串字符,不想让它运行直接Ctrl + C,光标就会跳入下一行. Tab: 这个键是最有用的键了,也是笔者敲击概 ...
- int 与 int *
#include <iostream>using namespace std;int QKPass(int* , int , int); //若声明为 int QKPass(int, i ...
- 一篇%3CDIV%20style%3D%22FONT-SIZE%
%3CDIV%20style%3D%22FONT-SIZE%3A%2016px%22%3E1%EF%BC%8C%E6%88%91%E4%BB%A5%E4%B8%BA%E7%BB%88%E6%9C%89 ...
- 如何创建一个一流的SDK?
怎么样的SDK算是一个好的SDK? 在做SDK的过程中我们走过非常多的弯路,是一个难以想象的学习过程,我们总结一个好的SDK应该具备的特质: 易用性,稳定性,轻量,灵活,优秀的支持. 一.易用性 因为 ...
- springboot问题:解决异常Unable to start embedded container;
使用eclipse创建springboot练习时,当主函数与控制器同时写在同一个类时,启动项目正常运行,而当把主函数单独放在一个类中时,无论是与控制器同包还是控制器所在的包是其子包,都报: org.s ...
- grunt中常见的插件
/** * 需要用到的文件夹有 js(src) css image html */ gulp是一种自动化构建工具,可以增强我们的工作流程,他是基于 Node.js 构建的,与gruntjs相比,gul ...
- 安装并配置Apache
从今天开始,我将开始Web开发的学习.本系列博客将陆续记录我学习过程中的收获和困惑,从前端到后端,一探Web开发的流程和内容.我目前掌握的有C/C++,有一些使用C进行嵌入式开发的经验,C++就马马虎 ...
- 微信支付生成带logo的二维码
利用到一个qrcode类 比较简洁 原作者没有加入二维码嵌入logo的功能 在这里我进行了小小的修改 可以实现生成微信支付二维码时打上logo 生成png格式的利用到该类中的png方法(我已经改好了) ...