ASP.NET Core 日志框架:Serilog

在 ASP.NET Core 日志模型 中对日志整体实现方式进行了介绍,通过使用内置日志记录器来实现日志的输出路径。而在实际项目开发中,使用第三方日志框架来记录日志也是非常多的,首先一般基础的内置日志记录器在第三方日志框架中都有实现,然后很多第三方日志框架在功能上更强大和丰富,能满足我们更多的项目分析和诊断的需求。

本文主要介绍 Serilog[1] 这个日志框架的使用,它是目前比较突出和受欢迎的一个日志框架。Serilog 在日志记录上采用 json 的格式,方便日志的快速查询与过滤。

默认日志行为

在使用 Serilog 之前我们先看看 ASP.NET Core API 项目默认的日志输出行为。创建基于 ASP.NET Core 3.1 的 API 项目后启动,默认访问会 weatherforecast 接口,然后可以从控制台和调试窗口看到如下输出日志 (ASP.NET Core API 项目模板默认集成了 ConsoleDebug 日志记录器):

日志输出等级在配置文件中有默认设置参数,appsettings.Development.json(开发环境)和 appsettings.json(生产环境)的 Logging 节点。下面以 appsettings.Development.json 为例:

{"Logging": {"LogLevel": {"Default": "Debug","System": "Information","Microsoft": "Information"}}
}

开发环境下默认输出 Debug 及以上等级日志,SystemMicrosoft 开头的日志 Information 及以上等级会输出,所以从上面的截图看到大批 Info 日志。

Serilog 使用

和内置日志记录器一样,Serilog 也有自己的日志记录器组件,NuGet 包命名上基本都是 Serilog.Sinks.xxx 的形式,如:Serilog.Sinks.ConsoleSerilog.Sinks.FileSerilog.Sinks.SeqSerilog.Sinks.ElasticSearch 等,接下来先以 Serilog.Sinks.Console 为例。

  1. 安装 Serilog.Settings.Configuration(读取配置)、Serilog.Sinks.Console(日志输出到控制台) NuGet 包 ;

  2. 配置文件添加 Serilog 配置,WriteTo 指定输出目标位置,它是一个数组类型,所以可以指定多个目标位置,这里暂时只指定输出到控制台:

    {"Serilog": {"MinimumLevel": {"Default": "Debug"},"WriteTo": [{ "Name": "Console" }]}
    }
    
  3. Program.cs 添加 UseSerilog,设置配置信息:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseSerilog((context, logger) =>{logger.ReadFrom.Configuration(context.Configuration);});webBuilder.UseStartup<Startup>();});
    
  4. 在 Startup.cs 的 Configure 请求管道中添加 UseSerilogRequestLogging

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseSerilogRequestLogging();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
    }
    
  5. 日志输出效果如下:

    由于日志总是输出一堆,并不能区分什么日志来自 Serilog ,其实 Serilog 输出的日志是非常简洁的,只有 HTTP GET ...  这一条,其他都是 AspNetCore 本身输出的。

日志输出简化

为了使日志输出更简洁,我们可以设置不输出 AspNetCore Info 日志,只需在 Serilog 配置节点中设置 AspNetCore 日志输出级别为 Warning

{"Serilog": {"MinimumLevel": {"Default": "Debug","Override": {"Microsoft.AspNetCore": "Warning"}},"WriteTo": [{ "Name": "Console" }]}
}

日志属性扩展

这部分我们将增加使用 Serilog.Sinks.Seq 日志记录器,不过在使用前需要先安装 Seq[2],和 ElasticSearch 类似,主要提供日志存储和查询,安装非常简单,这里不再赘述,参考 Windows or Docker[3]

Seq 服务启动后修改配置文件,在 WriteTo 节点内增加 Seq 的配置参数:

"WriteTo": [{ "Name": "Console" },{"Name": "Seq","Args": {"serverUrl": "http://localhost:5341/"}}
]

Seq 中查看日志如下,基于 ParentIdRequestIdTraceId 可实现链路跟踪:

手动记录的日志如下:

虽然通过之前的配置使一次请求中系统日志只保留了一条,但相比之下可能丢失了一些重要信息,如:endpointcontentType 等,所以我们需要在日志体上增加一些扩展属性,做到数量少但内容丰富。

前面在 Startup 的 Configure 方法中添加过 UseSerilogRequestLogging ,该方法还支持更多的参数配置,通过指定 EnrichDiagnosticContext 委托方法的实现向 IDiagnosticContext 对象中添加扩展属性,这些扩展属性将会作用于请求日志体中。(Serilog.AspNetCore 已将接口 IDiagnosticContext 作为单例添加到了 DI 容器中,可以在任何类中通过其 Set 方法添加扩展属性

public static class LogUtil
{public static void EnrichDiagnosticContext(IDiagnosticContext diagnosticContext, HttpContext httpContext){var request = httpContext.Request;diagnosticContext.Set("Host", request.Host);diagnosticContext.Set("ContentType", httpContext.Response.ContentType);var endpoint = httpContext.GetEndpoint();if (endpoint is object){diagnosticContext.Set("EndpointName", endpoint.DisplayName);}}
}
app.UseSerilogRequestLogging(opts =>
{opts.EnrichDiagnosticContext = LogUtil.EnrichDiagnosticContext;
});

Seq 中查看日志如下,已包含添加的扩展属性:

参考资料

[1]

Serilog: https://github.com/serilog/serilog-aspnetcore

[2]

Seq: https://datalust.co/seq

[3]

Windows or Docker: https://docs.datalust.co/docs/getting-started

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

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

相关文章

[C++STL]C++实现vector容器

代码如下: #pragma once #include <iostream> #include <assert.h> using namespace std;template<typename T> class Vector { public:typedef T* iterator;typedef const T* const_iterator;Vector() :_start(nullptr), _finish(nullptr), _endOfStorage(…

数据结构与算法--简单栈实现及其应用

栈 栈&#xff08;Stack&#xff09;是一种限制插入和删除只能在一个位置上进行的表&#xff0c;改位置是表的末端&#xff0c;叫做栈顶top。栈的基本操作有push &#xff08;进栈&#xff09;pop&#xff08;出栈&#xff09;栈又叫做LIFO&#xff08;后进先出&#xff09;表…

树莓派销量突然猛增

树莓派基金会负责人 Eben Upton 近日在推特上公布&#xff0c;树莓派在三月份的销量达到 640,000 套&#xff0c;三月也成为有史以来销售量第二高的时期。Eben 认为&#xff0c;这大概是由于受 COVID-19 冠状病毒肺炎影响&#xff0c;在居家隔离期间&#xff0c;树莓派能够以低…

[C++STL]C++实现list容器

代码如下&#xff1a; #pragma once #include <iostream> using namespace std;template<typename T> struct ListNode {T _data;ListNode<T> *_next;ListNode<T> *_prev;ListNode(const T & val T()) :_data(val), _next(nullptr), _prev(nullp…

Java动态追踪技术--BTrace

Java动态追踪技术 需求翻译官的日常工作不是在的改bug&#xff0c;发布代码&#xff0c;就是在加日志查找bug的路上。查BUG的过程是痛苦的&#xff0c;我们总是在不停的看代码&#xff0c;修改代码&#xff0c;添加日志&#xff0c;从而帮助我们发现问题&#xff0c;这种形式是…

C#黔驴技巧之实现统计结果排名

本节是实现统计排名的一点技巧&#xff0c;可能有一部分童鞋在实现排名统计绕了一大圈&#xff0c;最后还不如两行代码就搞定&#xff0c;代码清晰而可读性强&#xff0c;接下来我们来一起来看看。我们知道在SQL Server中可以利用Row_Number、Rank等函数进行排名&#xff0c;在…

数据结构与算法--二叉树实现原理

二叉树 二叉树&#xff08;binary tree&#xff09;是一棵树&#xff0c;其中每个节点都不能有多于两个的子节点二叉树的一个性质是一颗平均二叉树的深度要比节点个数N小得多&#xff08;重点&#xff09;&#xff0c;对二叉树的分析得出其平均深度为O(N\sqrt NN​)&#xff0…

[C++STL]C++实现stack容器适配器

代码如下: #include <iostream> #include <deque> using namespace std;template<typename T,typename Con deque<T>> class Stack { public:Stack() {}void Push(const T &x) {_c.push_back(x);}void Pop(){_c.pop_back();}T & Top(){retur…

Istio Pilot架构解析

本文节选自 ServiceMesher 社区联合编写的《Istio Handbook——Istio 服务网格进阶实战》。本书地址&#xff1a;https://github.com/servicemesher/istio-handbook/在应用从单体架构向微服务架构演进的过程中&#xff0c;微服务之间的服务发现、负载均衡、熔断、限流等服务治理…

数据结构与算法--二叉查找树实现原理

二叉查找树 二叉树的一个重要应用就是他在查询中的使用&#xff0c;假设书中每个节点存储一项数据。在我们的案例中&#xff0c;任意复杂的项在java中都容易处理&#xff0c;但为了简单还是假设都是整数。还假设他们都是不重复的整数&#xff0c;使二叉树称为二叉查找树的性质…

[C++STL]C++实现queue容器适配器

代码如下: #include <iostream> #include <deque>using namespace std;template<typename T,typename Con deque<T>> class Queue { public:Queue(){}void Push(const T & x){_c.push_back(x);}void Pop(){_c.pop_front();}T &Back(){return…

当模板方法遇到了委托函数,你的代码又可以精简了

现如今当你翻看一些开源项目源码的时候&#xff0c;你会发现现在到处充斥着委托函数&#xff0c;如Func,Action,Predicate&#xff0c;确实现在的C#在函数式编程 的路上越来越成为主流&#xff0c;越来越显示威力&#xff0c;曾经的一些经典设计模式写法&#xff0c;在函数式下…

数据结构与算法--面试必问AVL树原理及实现

数据结构与算法–AVL树原理及实现 AVL&#xff08;Adelson-Velskii 和landis&#xff09;树是带有平衡条件的二叉查找树&#xff0c;这个平衡条件必须容易实现&#xff0c;并且保证树的深度必须是O(logN)。因此我们让一棵AVL树中每个节点的左子树和右子树的高度最多相差1&…

MySQL8.0新特性

在这之前Mysql的版本是5.7&#xff0c;也是目前使用最广泛的一个版本。现在新版本跳过了6和7直接来到了8&#xff0c;那么V6和V7版本去哪里了呢&#xff1f;比较靠谱的说法是v6用作了内部的其他用途而v7的话是因为mysql有个产品叫做clusterdb他有7这个版本&#xff0c;所以这个…

数据结构与算法--B树原理及实现

B树 前几篇文中讨论的数据结构我们都是假设所有的数据都存储在计算机的主存中。可说总要那么海量的数据需要通过个中数据结构去存储&#xff0c;我们不可能有这么多内存区存放这些数据。那么意味着我们需要将他们放磁盘。所以这个时候范问时间复杂度O决定了他是否能适合存储磁盘…

[C++STL]C++实现priority_queue容器适配器

代码如下: #pragma once #include <iostream> #include <vector> using namespace std;template<typename T> struct Less {bool operator()(const T &a, const T &b){return a < b;} };template<typename T> struct Greater {bool operat…

为什么要用内插字符串代替string.format

知道为什么要用内插字符串&#xff0c;只有踩过坑的人才能明白&#xff0c;如果你曾今使用string.format超5个以上占位符&#xff0c;那其中的痛苦我想你肯定是能够共鸣的。一&#xff1a;痛苦经历先上一段曾今写过的一段代码&#xff0c;大家来体会一下&#xff1a;LogHelper.…

SpringCloud Alibaba 框架下公司架构图

上一篇&#xff1a;Docker容器实战思维 下一篇&#xff1a;分布式事务理论模型

C++ 实现堆

代码如下(小根堆): #include <iostream> #include <assert.h> using namespace std;//小根堆,堆排序从大到小排 class Heap { public:Heap():data(nullptr), size(0), capacity(0) {}~Heap(){delete[] data;data nullptr;size 0;capacity 0;}/*void Swap(int *…

iPhone上运行Linux也要来了

FOSSBRTES 报道称&#xff0c;用户很快将可以通过双启动功能&#xff0c;像在 Android 设备上那样在 iPhone 上运行 Linux。目前&#xff0c;iOS 越狱极客、开发人员 Raffaele 以及 mcg29 已在其 Github 页面上发布了详细说明&#xff0c;详细介绍了如何双启动 64 位 iOS 设备。…