.NET Core 开发日志:结构化日志

2019 年 1 月 14 日 DotNet

(给DotNet加星标,提升.Net技能


转自:Ken.W

cnblogs.com/kenwoo/p/9906185.html


在.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类型,同时设置includeAllProperties为true,以显示所有对象属性。


<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平台日志记录框架:NLog+Mysql

.NET Core使用NLog通过Kafka实现日志收集

.NET Core集成Exceptionless分布式日志功能


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

喜欢就点一下「好看」呗~

登录查看更多
0

相关内容

【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
56+阅读 · 2020年6月26日
干净的数据:数据清洗入门与实践,204页pdf
专知会员服务
160+阅读 · 2020年5月14日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
115+阅读 · 2020年5月10日
【SIGMOD2020-腾讯】Web规模本体可扩展构建
专知会员服务
29+阅读 · 2020年4月12日
【图神经网络(GNN)结构化数据分析】
专知会员服务
114+阅读 · 2020年3月22日
【2020新书】Kafka实战:Kafka in Action,209页pdf
专知会员服务
65+阅读 · 2020年3月9日
在K8S上运行Kafka合适吗?会遇到哪些陷阱?
DBAplus社群
9+阅读 · 2019年9月4日
携程用ClickHouse轻松玩转每天十亿级数据更新
DBAplus社群
11+阅读 · 2019年8月6日
一文看懂怎么用 Python 做数据分析
大数据技术
23+阅读 · 2019年5月5日
Python用于NLP :处理文本和PDF文件
Python程序员
4+阅读 · 2019年3月27日
一天精通无人中级篇:遥控器协议 S-BUS
无人机
48+阅读 · 2018年12月20日
.NET Core 环境下构建强大且易用的规则引擎
干货|全文检索Solr集成HanLP中文分词
全球人工智能
4+阅读 · 2017年8月27日
Arxiv
7+阅读 · 2018年1月31日
VIP会员
相关VIP内容
相关资讯
在K8S上运行Kafka合适吗?会遇到哪些陷阱?
DBAplus社群
9+阅读 · 2019年9月4日
携程用ClickHouse轻松玩转每天十亿级数据更新
DBAplus社群
11+阅读 · 2019年8月6日
一文看懂怎么用 Python 做数据分析
大数据技术
23+阅读 · 2019年5月5日
Python用于NLP :处理文本和PDF文件
Python程序员
4+阅读 · 2019年3月27日
一天精通无人中级篇:遥控器协议 S-BUS
无人机
48+阅读 · 2018年12月20日
.NET Core 环境下构建强大且易用的规则引擎
干货|全文检索Solr集成HanLP中文分词
全球人工智能
4+阅读 · 2017年8月27日
Top
微信扫码咨询专知VIP会员