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

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

什么是结构化日志?

当前互联网、物联网、大数据突飞猛进,软件越复杂,查找任何给定问题的起因就越困难(且成本更高)。

在实践中我们开发了各种规避、诊断应用程序错误行为的利器:静态类型检查自动化测试事件探查器崩溃转储监视系统。但是记录程序执行步骤的日志仍然是事后诊断最丰富的数据源。

在日志分析时,小批量普通的文本对于人类很友好,但却很难从大量普通文本中快速定位、精准提取特定信息。

.....
[2018-04-07T13:45:56.789Z INF] https://example.com/api/warehouse,query reserve,took 100 ms
[2018-04-07T13:45:56.789Z INF] api/commitOrder,OrderId:9876543210,commit order took 50 ms
......
[2018-04-07T13:45:56.789Z INF] /login,user:Kenny,from ip_address:127.0.0.1,took 100 ms
......
[2018-04-07T13:45:56.789Z INF] https://example.com/api/warehouse,OrderId:9876543210,decrease reserve took 10000 ms
[2018-04-07T13:45:56.789Z INF] /api/creatNewOrder,OrderId:9876543210, create order took 100 ms
.....
  • 如果找到特定OrderId?

  • 如何找到哪些请求耗时较长(比如大于2S)?

  • 如何定位到该耗时请求处理管道中哪一段出现性能瓶颈?

  • 出现性能瓶颈的请求占比?

普通文本对人类友好,对于机器不友好。结构化日志提出了Message template来解决日志对机器不友好的问题。

Messgae Template: 是一个与语言无关的规范,捕获、(以对人类和机器友好的格式)呈现结构化的日志。

var traceid = _.TraceIdentifier;// 【锁定库存】 这个动作耗时较长
_logger.LogInformation("{TraceId},{endpoint},OrderId:{orderId},decrease reserve took {elasped} ms", traceid, "https://example.com/api/warehouse", 9876543210, 10000);

注意命名占位符,它们能如格式化字符串占位符{0}{1}一样占位,而且能将属性名称与每个匹配位置的消息数据相关联,如下图以json格式提取了关键消息。

消息模板的优势在于:既能保持普通文本的格式,又具备捕获结构化数据的能力(对机器友好)。


下面来完整输出、分析提交订单请求的日志:

利用NLog向ES输出结构化日志

NLog4.5引入结构化日志,支持Message Template, 在ASP.NET Core脚手架Startup文件--->Configure方法添加如下代码:

 app.MapWhen(_ => _.Request.Path.Value == "/" ,appBuilder => appBuilder.Run(_ =>{var traceid = _.TraceIdentifier;// 查询库存_logger.LogInformation("{traceId},{endpoint},query reserve,took{elasped} ms", traceid, "https://example.com/api/warehouse", 100);// 创建订单_logger.LogInformation("{traceId},{endpoint},OrderId:{orderId}, create order took {elasped} ms", traceid, "/api/creatNewOrder", 9876543210, 100);// 锁定库存_logger.LogInformation("{traceId},{endpoint},OrderId:{orderId},decrease reserve took {elasped} ms", traceid, "https://example.com/api/warehouse", 9876543210, 10000);// 提交订单_logger.LogInformation("{traceId},{endpoint},OrderId:{orderId},commit order took {elasped} ms", traceid, "api/commitOrder", 9876543210, 50);_.Response.StatusCode = StatusCodes.Status200OK;_.Response.WriteAsync("Generate Order OK!");  return Task.CompletedTask;}));

这里我们关注如何向ElasticSearch输出结构化日志,请务必将includeAllProperties="true",这样输出到ES的才会包含所有事件属性。

<target name="elastic" xsi:type="BufferingWrapper" flushTimeout="5000"><target xsi:type="ElasticSearch" includeAllProperties="true" index="logstash-20200805"  uri="${configsetting:item=ConnectionStrings.ElasticUrl}" /></target>    

Kibana中分析日志

这个订单请求,会产生6条日志(这里你也会看到日志的显示顺序可能不能如你所愿):下面给出[锁定库存]的日志,ES文档上已经出现了关键的消息属性[traceId] [endpoint] [orderId] [elasped]

{"_index": "logstash-20200805","_type": "logevent","_id": "emTivXMBwcdwe4RliB9f","_version": 1,"_score": null,"_source": {"@timestamp": "2020-08-05T17:10:00.7170456+08:00","level": "Info","message": "2020-08-05 17:10:00.7170|INFO|EqidManager.Startup|0HM1P3TAGNJ5Q:00000001,https://example.com/api/warehouse,OrderId:9876543210,decrease reserve took 10000 ms","traceId": "0HM1P3TAGNJ5Q:00000001","endpoint": "https://example.com/api/warehouse","orderId": 9876543210,"elasped": 10000},"fields": {"@timestamp": ["2020-08-05T09:10:00.717Z"]},"sort": [1596618600717]
}

通过Kibana界面我们可以便捷地完成如下分析:

  1. 通过{TraceId}找到某次请求所有日志

  2. 通过{elasped} >=10s 过滤出处理时长大于10s的阶段

  3. 通过{ordeid} 追踪该订单完整链路
    ......


总结

本文肝时较长(elasped>=10天)

  • 从常规诊断日志谈到[对机器友好,适用于分析的结构化日志],其中的核心是消息模板。

  • 再谈到我是如何利用NLog输出结构化日志,其中注意在NLog Target中设置includeAllProperties=true(默认是false), 摸索了很久

  • 最后在Kibana中演示便捷的分析结构化日志

干货周边也很重要

  1. [消息模板]    https://messagetemplates.org/

  2. [如何利用NLog输出结构化日志]   https://github.com/nlog/nlog/wiki/How-to-use-structured-logging

  3. [NLog to ES]   https://github.com/markmcdowell/NLog.Targets.ElasticSearch

  4. [被忽略的TraceId,可以用起来了]

  5. Logging with ElasticSearch, Kibana, ASP.NET Core and Docker

如果本文对你有帮助,

不妨来个分享、点赞、在看三连

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++中字符串的截取 str.substr(a,b);

C中字符串的截取 str.substr(a,b); #include<bits/stdc.h> using namespace std;int main() {//str.substr(a,b);a表示截取字符串的下标&#xff0c;b表示要截取的长度不填则为截取的到最后 string str "aaaabc";cout << str.substr(3) << endl;…

java打印设备集中管理_Kafka+Log4j实现日志集中管理

记录如何使用KafkaLog4j实现集中日志管理的过程。引言前面写的《SpringLog4jActiveMQ实现远程记录日志——实战分析》得到了许多同学的认可&#xff0c;在认可的同时&#xff0c;也有同学提出可以使用Kafka来集中管理日志&#xff0c;于是今天就来学习一下。特别说明&#xff0…

7-27 家谱处理 (30 分)(详解+map做法)map真香啊

一&#xff1a;题目 人类学研究对于家族很感兴趣&#xff0c;于是研究人员搜集了一些家族的家谱进行研究。实验中&#xff0c;使用计算机处理家谱。为了实现这个目的&#xff0c;研究人员将家谱转换为文本文件。下面为家谱文本文件的实例&#xff1a; John Robert Frank Andr…

微软开源基于 Envoy 的服务网格 Open Service Mesh

原文地址&#xff1a;https://techcrunch.com/2020/08/05/microsoft-launches-open-service-mesh/Open Service Mesh&#xff08;OSM&#xff09;是一个轻量级的、可扩展的、云原生的服务网格&#xff0c;它允许用户对高度动态的微服务环境进行统一管理、安全保护&#xff0c;并…

java servlet jsp javabean关系图_Servlet+JSP+JavaBean开发模式(MVC)介绍

好伤心...写登陆注册之前看见一篇很好的博文&#xff0c;没有收藏&#xff0c;然后找不到了。前几天在知乎上看见一个问题&#xff0c;什么时候感觉最无力。前两天一直想回答&#xff1a;尝试过google到的所有solve case&#xff0c;结果bug依然在。今天想回答&#xff1a;明明…

7-28 搜索树判断 (25 分)(思路加详解) just easy!

一&#xff1a;题目 对于二叉搜索树&#xff0c;我们规定任一结点的左子树仅包含严格小于该结点的键值&#xff0c;而其右子树包含大于或等于该结点的键值。如果我们交换每个节点的左子树和右子树&#xff0c;得到的树叫做镜像二叉搜索树。 现在我们给出一个整数键值序列&…

Azure DevOps+Docker+Asp.NET Core 实现CI/CD(一 .简介与创建自己的代理池)

前言本文主要是讲解如何使用Azure DevOpsDocker 来实现持续集成Asp.NET Core项目(当然 也可以是任意项目).打算用三个篇幅来记录完整的全过程觉得有帮助的朋友~可以左上角点个关注,右下角点个推荐CI/CD简介首先,我们先来简单的介绍一下什么是CI/CDCI全拼Continuous Integration…

7-31 笛卡尔树(25分)(题目分析+简单算法+详解+思路)

一&#xff1a;题目 7-31 笛卡尔树 (25 分) 笛卡尔树是一种特殊的二叉树&#xff0c;其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树&#xff0c;即结点左子树的所有K1值都比该结点的K1值小&#xff0c;右子树则大。其次所有结点的K2关键字满足优先队列&#…

java ee导入后乱码_JavaEE中为什么出现中文乱码?

1.原因客户端通过IE提交时用的默认编码是UTF-8&#xff0c;而当我们用Myeclipse的时候的服务端接受的时候用的是iso-8859-12.解决方法服务端也用UTF-8编码byte[] result request.getParameter("titile").getBytes("iso-8859-1") ;title new String(resu…

Java多线程之龟兔赛跑和抢票

一&#xff1a;引言 练习这个码主要是为了体验在实现 多线程的方式中 实现Runable接口的好处&#xff0c;其中之一 有共享资源 &#xff0c;一个实现类但可以有多个代理 二&#xff1a;龟兔赛跑 package com.wyj.one; /*** 实现Runable接口也就是可以共享资源* author 86155…

不仅性能秒杀Hadoop,现在连分布式集群功能也开源了

就在昨天&#xff08;2020年8月3日&#xff09;&#xff0c;涛思数据团队正式宣布&#xff0c;物联网大数据平台TDengine集群版开源。此次开源&#xff0c;我们在GitHub上传了23.9万行源代码&#xff0c;1198个源文件&#xff0c;包含我自己疫情期间写的一万余行C代码&#xff…

php 将中文字符转英文字母_php 中英文语言转换类

起初想到制成XML文档形式&#xff0c;这样操作也起来很容易。只是看到说XML效率不怎样再者就是不同的模板&#xff0c;可这样也有个小问题&#xff0c;有些词汇比如时间提示是不确定&#xff0c;与可能是minute &#xff0c;day。也有可能复数加 s那好吧&#xff0c;做成数组&a…

7-32 哥尼斯堡的“七桥问题” (25 分)(思路+详解+题目分析)两种做法任选其一

一&#xff1a;题目&#xff1a; 哥尼斯堡是位于普累格河上的一座城市&#xff0c;它包含两个岛屿及连接它们的七座桥&#xff0c;如下图所示。 可否走过这样的七座桥&#xff0c;而且每桥只走过一次&#xff1f;瑞士数学家欧拉(Leonhard Euler&#xff0c;1707—1783)最终解…

.NET 异步详解(更新)

前言博客园&#xff08;cnblogs.com&#xff09;中有很多关于 .NET async/await 的介绍&#xff0c;但是很遗憾&#xff0c;很少有正确的&#xff0c;甚至说大多都是“从现象编原理”都不过分。最典型的比如通过前后线程 ID 来推断其工作方式、在 async 方法中用 Thread.Sleep …

Java中关于单核处理多个线程的认识与了解

一&#xff1a;单核执行多线程 1. 首先要知道 进程&#xff0c;线程&#xff0c;程序进程&#xff1a;执行中的程序叫做进程(Process)&#xff0c;是一个动态的概念&#xff0c;在一个进程中包含多个线程线程&#xff1a;指的是一条执行路径程序&#xff1a;就是静态的代码块2…

一次简单的服务器 cpu 占用率高的快速排查实战

前两天&#xff0c;朋友遇到一个线上 cpu 占用率很高的问题&#xff0c;我们俩一起快速定位并解决了这个问题。在征求朋友同意后&#xff0c;特发此文分享整个过程。本文以对话的形式展开&#xff0c;加上我的内心独白。文中对话与实际对话略有出入。友&#xff1a; 在吗&#…

php文件直链源码,PHP萌心上传直链外链网盘源码

源码说明PHP萌心上传直链外链网盘源码&#xff0c;小巧单文件&#xff0c;无需数据库&#xff0c;只需PHP运行环境即可。源码安装方法上传文件到PHP运行环境&#xff0c;修改index.php内的配置// 单个文件限制$max_file_size"51200";//大小指的KB&#xff0c;51200是…

7-33 地下迷宫探索 (30 分)(思路加详解)

一&#xff1a;题目 7-33 地下迷宫探索 (30 分)地道战是在抗日战争时期&#xff0c;在华北平原上抗日军民利用地道打击日本侵略者的作战方式。地道网是房连房、街连街、村连村的地下工事&#xff0c;如下图所示。 我们在回顾前辈们艰苦卓绝的战争生活的同时&#xff0c;真心钦…

php如何判断二维数组为空,PHP判断数组为空的具体方式

在学习PHP语言的时候&#xff0c;初学者往往会对数组感到一些棘手。不过在通过深入的学习之后&#xff0c;我们会发现&#xff0c;这些其实并没有想象中的困难。我们今天就要向大家介绍PHP判断数组为空的具体方式&#xff0c;希望能让新手们了解一些新知识。PHP判断数组为空之一…

联通定时休眠5G基站 戳破皇帝的新衣

近年来&#xff0c;5G被欧美政客、大公司、媒体连番炒作&#xff0c;在公开舆论上&#xff0c;5G成为了“科技制高点”&#xff0c;成为决定国家命运的“外星科技”&#xff0c;个别明星企业家还声称&#xff0c;“5G改变社会”&#xff0c;“5G应用后美国将成为落后国家”。但…