.NET Worker Service 添加 Serilog 日志记录

前面我们了解了 .NET Worker Service 的入门知识1如何优雅退出 Worker Service 2,今天我们接着介绍一下如何为 Worker Service 添加 Serilog 日志记录。

在实际的生产环境中,应用程序中记录日志是非常宝贵的。在许多情况下,开发人员无法直接访问生产环境来调试问题。高质量的日志记录为解决线上问题提供了线索和依据。

日志记录是将应用程序操作和状态记录到辅助接口的过程。

.NET 日志记录框架

.NET 中有很多默认的日志记录提供程序3,它们可以将日志输出到控制台、Debug、EventSource 和 EventLog 等,例如在上一篇的示例中,默认的实现是将日志记录输出到了控制台窗口。

但是 .NET 中没有可以帮我们将日志信息输出到文件和数据库的内置提供程序,而这却是我们在生产环境中十分常见的应用场景。为了实现这一功能,我们需要为 .NET 实现自定义的日志记录提供程序4,这需要大量时间,因为需要考虑很多事情,比如读写性能、存储空间、配置等等。

幸运的是,一些优秀的第三方程序包可以为我们提供帮助,.NET 中三种最流行的日志框架分别是:log4netNLogSerilog,我们只需从 NuGet 包存储库中获取它们,然后简单地配置一下便可以愉快地使用它们了。

log4net

log4net5 是一个始于 2001 年的领先的日志记录框架,最初是 Java 框架 log4j 的端口。多年来,Apache Logging Services 项目持续进行开发,没有其他框架能像 log4net 一样久经考验。log4net 是所有现代 .NET 日志记录框架的鼻祖,在日志框架中,日志级别(log levels)、记录器(logger)和输出模块(appenders/targets/sinks)等概念几乎都是通用的6。相信所有多年使用 .NET 编程的朋友对 log4net 都相当熟悉。

log4net 好用、稳定且灵活,但是它的配置相对来说比较复杂一些,而且很难实现结构化的日志记录。

NLog

NLog7 也是一个相当老的项目,最早的版本发布于 2006 年,不过目前仍在积极开发中。NLog 从 v4.5 版本开始新增了对结构化日志记录的支持

与 log4net 相比,NLog 的配置更加容易,并且基于代码的配置也比较简洁。NLog 中的默认设置比 log4net 中的默认设置会更合理一些。需要注意的一点是,当使用这两个框架,您可能会遇到同一个问题,那就是配置有问题(比如忘记复制配置文件)时,不会得到任何提示,也不会输出日志信息。假如您将应用部署上线以后遇到这个情况,这将是致命的,因为许多问题的检查都是依赖于日志记录的。当然,这么设计的初衷是避免让应用程序因日志问题而导致崩溃。

Serilog

Serilog8 日志记录框架发布于 2013 年,相对来说是一个较新的框架。与其他日志框架不同的是,Serilog 在设计时考虑了强大的结构化事件数据,提供了开箱即用的结构化日志实现。所以 Serilog 对结构化日志的支持非常好,而且配置简洁。Serilog 中的日志可以发送到许多终端,Serilog 称这些终端为“输出模块库(sinks)”。您可以在 https://github.com/serilog/serilog/wiki/Provided-Sinks 页面查看非常全面的列表。

Serilog 中还有一个功能强大的概念是Enricher,可以通过各种方式来丰富日志事件的属性,从而向日志添加新的信息。NuGet 中提供了一些预建的 Enricher,您也可以通过实现 ILogEventEnricher 构建自己的 Enricher。

结构化日志记录

或许您已注意到了,前面我多次提到结构化日志记录,那么什么是结构化日志记录,为什么我要强调结构化日志记录呢?

通常情况下,您会发现日志信息基本上包含两部分内容:消息模板,而 .NET 通常只接受诸如 string.Format(...) 这样的的输入字符串。比如:

var position = new {
    Latitude = 25, Longitude = 134 };
var elapsedMs = 34;

log.Information("Processed Position, Latitude:{0}, Longitude: {1} in Elapsed:{2} ms.", position.Latitude, position.Longitude, elapsedMs);

这条日志只是简单地被转换为文本输出到日志文件中:

[INF] Processed Position, Latitude:25, Longitude: 134 in Elapsed:34 ms.

这看起来很好,但它可以更好!

当我们遇到问题的时候,我们需要根据一些已知的信息来检索日志记录。比如,假设我们已知 Latitude 为 25,Longitude 为 134,我们要查找这条日志的话,该怎么做呢?由于上面输出的日志信息是简单的文本,有经验的您可能立马会想到使用正则表达式或者简单的字符串匹配,但这样不仅不够直观,实现起来也比较麻烦。有没有更好的方法呢?

如果我们在存储日志的时候,将其中包含值的部分作为特征提取出来,形成由键和值组成的有结构的 JSON 对象,作为每条日志记录的属性(properties):

{
   "Position": {
   "Latitude": 25, "Longitude": 134}, "Elapsed": 34}

然后,在我们检索的时候只需要查找日志记录的 properties 就可以了,它是结构化的,检索起来既方便又直观。

Serilog 帮我们实现了这一点,您只需改动一行代码就可以了:

log.Information("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

Position 前

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值