在.NET生态圈中,最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net。而其后来者,莫过于NLog。Nlog与log4net相比,有一项较显著的优势,它支持结构化日志。

结构化日志,也被称为语义化日志。其作用有二,利于查询与方便分析。

当系统上线被广泛使用或者时间久远之后,日志的大量出现不可避免。且日志本身作为一种数据,也有其重要的价值。因此,如何有效地对其进行查询以及最大价值化地分析处理便成了一个重要的问题。

非结构化日志

对于日志的处理,需要权衡对开发者的友好性与对程序解析的方便性。传统的非结构化日志更倾向于前者。当开发者想要记录一段日志时,他可以很简单地加上一行代码,即可达成其目的。

logger.Info("Logon by user:{0} from ip_address:{1}", "Kenny", "127.0.0.1");

然后这行代码的执行结果可能被存于文本文件或者数据库中。

2018-12-22 16:29:29.2793|Info|Logon by user:Kenny from ip_address:127.0.0.1

这样的日志以开发者的角度来看,清晰易懂,十分友好。但如果要使用程序去查找海量的上述例子里的某段时间内的特定登陆用户,则很难高效地完成这一要求,因为需要对每个日志进行字符串解析。

消息模板

消息模板规范是结构化日志的通用语法,其是一个与开发语言无关的规范,能以特定格式捕获及呈现结构化日志,同时提供对开发者与程序解析的友好支持。

以上图片中的日志记录方式乍看起来与非结构化日志差不多,但它们之间具有本质的区别,在结构化日志里,是以对象而非字符串处理日志内容的。

如果将非结构化日志例子里的代码改成结构化日志的写法。

logger.Info("Logon by {user} from {ip_address}", "Kenny", "127.0.0.1");

执行后两者的结果是这样的:

2018-12-22 16:29:29.2793|Info|Logon by user:Kenny from ip_address:127.0.0.1
2018-12-22 16:29:29.2976|Info|Logon by "Kenny" from "127.0.0.1"

似乎差别并不大,再将输出类型改成JSON风格看看:

{ "time": "2018-12-22 16:30:15.1314", "level": "INFO", "message": "Logon by user:Kenny from ip_address:127.0.0.1" }
{ "time": "2018-12-22 16:30:15.1569", "level": "INFO", "message": "Logon by \"Kenny\" from \"127.0.0.1\"", "user": "Kenny", "ip_address": "127.0.0.1" }

显而易见,对于后者,因为user被作为对象的属性独立分离出来,在做程序处理时,可以很方便地以其为条件进行筛选。这对于查询或者分析日志是极为重要的。

NLog

NLog中对于结构化日志的支持是在4.5版本才开始的。这一改动并不会破坏原有的代码,而如果想要使用新的特性,则只要用符合消息模板的语法编写所需的代码即可。

Object o = null;

logger.Info("Test {value1}", o); // null case. Result:  Test NULL
logger.Info("Test {value1}", new DateTime(2018,03, 25)); // datetime case. Result: Test 25-3-2018 00:00:00 (locale TString)
logger.Info("Test {value1}", new List<string> { "a", "b" }); // list of strings. Result: Test "a", "b"
logger.Info("Test {value1}", new[] { "a", "b" }); // array. Result: Test "a", "b"
logger.Info("Test {value1}", new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } }); // dict. Result: Test "key1"=1, "key2"=2 var order = new Order
{
OrderId = 2,
Status = OrderStatus.Processing
}; logger.Info("Test {value1}", order); // object Result: Test MyProgram.Program+Order
logger.Info("Test {@value1}", order); // object Result: Test {"OrderId":2, "Status":"Processing"}
logger.Info("Test {value1}", new { OrderId = 2, Status = "Processing"}); // anomynous object. Result: Test { OrderId = 2, Status = Processing }
logger.Info("Test {@value1}", new { OrderId = 2, Status = "Processing"}); // anomynous object. Result:Test {"OrderId":2, "Status":"Processing"}

代码的格式化结果依据数据的类型而定。

  • 字符串类型将被双引号包围
  • 数值类型没有引号
  • null显示为NULL
  • 列表或数组类型,以逗号分隔,例如:"item1", "item2"
  • 字典类型,健与值之间用等号相联,例如:"key1"="value1", "key2"="value2"
  • 对象类型,调用ToString方法显示结果

此外,还可以有@与$符号:

  • @ 以JSON格式格式化数据
  • $ 强制调用ToString方法

而将日志输出格式改成JSON的方法,是在NLog.config配置文件里将布局切换成JsonLayout类型,同时设置includeAllPropertiestrue,以显示所有对象属性。

<target name="console" xsi:type="Console">
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>

Serilog

能够实现结构化日志的类库除了NLog之外,其它较常用的当属Serilog。

与NLog相比,Serilog省去了配置文件,直接使用代码,实现方式更加简洁。

var position = new { Latitude = 25, Longitude = 134 };
var elapsedMs = 34; var log = new LoggerConfiguration()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
log.Information("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

执行结果:

{"Timestamp":"2018-12-22T17:15:23.6389082+08:00","Level":"Information","MessageTemplate":"Processed {@Position} in {Elapsed:000} ms.","Properties":{"Position":{"Latitude":25,"Longitude":134},"Elapsed":34},"Renderings":{"Elapsed":[{"Format":"000","Rendering":"034"}]}}

.NET Core开发日志——结构化日志的更多相关文章

  1. 探索ASP.Net Core 3.0系列六:ASP.NET Core 3.0新特性启动信息中的结构化日志

    前言:在本文中,我将聊聊在ASP.NET Core 3.0中细小的变化——启动时记录消息的方式进行小的更改. 现在,ASP.NET Core不再将消息直接记录到控制台,而是正确使用了logging 基 ...

  2. .NET下使用 Seq结构化日志系统

    前言 我们公司在日志管理方面一直没有统一,主要痛点有: 每个开发人员都是各用各的,存储日志的形式也是五花八门,如:本地文件,数据库,Redis,MongoDB 由于公司访问服务器要通过堡垒机,所以本机 ...

  3. 结构化日志类库 ---- Serilog库

    在过去的几年中,结构化日志已经大受欢迎.而Serilog是 .NET 中最著名的结构化日志类库 ,我们提供了这份的精简指南来帮助你快速了解并运用它. 0. 内容 设定目标 认识Serilog 事件和级 ...

  4. 如何利用NLog输出结构化日志,并在Kibana优雅分析日志?

    上文我们演示了使用NLog向ElasticSearch写日志的基本过程(输出的是普通文本日志),今天我们来看下如何向ES输出结构化日志.并利用Kibana中分析日志. NLog输出结构化日志 Elas ...

  5. Asp.Net Core中利用Seq组件展示结构化日志功能

    在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...

  6. Beats: 使用 Filebeat 进行日志结构化

    文章转载自:https://blog.csdn.net/UbuntuTouch/article/details/106688240 生产一个叫做 json_logs 的文件: {"user_ ...

  7. [AI开发]视频结构化类应用的局限性

    算法不是通用的,基于深度学习的应用系统不但做不到通用,即使对于同一类业务场景,还需要为每个场景做定制.特殊处理,这样才能有可能到达实用标准.这种局限性在计算机视觉领域的应用中表现得尤其突出,本文介绍基 ...

  8. 【转】结构化日志类库 ---- Serilog库

    源地址:https://www.cnblogs.com/mq0036/p/8479956.html 解决异常: Invalid cast from 'System.String' to 'Serilo ...

  9. 重新整理 .net core 实践篇—————日志系统之结构化[十八]

    前言 什么是结构化呢? 结构化,就是将原本没有规律的东西进行有规律话. 就比如我们学习数据结构,需要学习排序然后又要学习查询,说白了这就是一套,没有排序,谈如何查询是没有意义的,因为查询算法就是根据某 ...

随机推荐

  1. android 制作9.png图

    9.png图片与.png图片的具体不同之处.9.png图片的四周与普通的png图片相比多了一个像素位的白色区域,该区域只有在图片被还原和制造的时候才能看到,当打包后无法看见,并且图片的总像素会缩小2个 ...

  2. ceph rgw multisite基本用法

    Realm: Zonegroup: 理解为数据中心,由一个或多个Zone组成,每个Realm有且仅有 一个Master Zonegroup,用于处理系统变更,其他的称为Slave Zonegroup, ...

  3. Effective Java 第三版——47. 优先使用Collection而不是Stream来作为方法的返回类型

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. Sql Server 阻塞的常见原因和解决办法

    1. 由于语句运行时间太长而导致的阻塞,语句本身在正常运行中,只须等待某些系统资源 解决办法: a. 语句本身有没有可优化的空间 b. Sql Server 整体性能如何,是不是有资源瓶颈影响了语句执 ...

  5. C#-MVC开发微信应用(2)--微信消息的处理和应答

    微信应用使用场景和商机很多,所以这也是一个技术的方向,因此,有空研究下.学习下微信的相关开发,也就成为SNF完善的必要条件了.本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验 ...

  6. ECMAScript 6 入门之Proxy代理和set

    1.Proxy代理 1. var user={ full_name:function () { return this.fname+" "+this.lname; } }; use ...

  7. 每日英语:Mystery Medical Symptoms Hit a Surprising Number of Patients

    It's a common scenario: You have an odd pain for weeks or are feeling too tired lately. So you head ...

  8. 给dubbo接口添加白名单——dubbo Filter的使用

    本文转自:http://blog.csdn.net/mj158518/article/details/47379799 在开发中,有时候需要限制访问的权限,白名单就是一种方法.对于Java Web应用 ...

  9. 配置Django

    第一步,安装Python,在这里下载,如果你安装在C:\Python27, 把C:\Python27;C:\Python27\Scripts;C:\Python27\Lib 加到你的Path 第二步: ...

  10. Java多线程的同步机制(synchronized)

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...