.NET Core开发实战(第19课:日志作用域:解决不同请求之间的日志干扰)--学习笔记
19 | 日志作用域:解决不同请求之间的日志干扰
开始之前先看一下上一节的代码
// 配置的框架
var configBuilder = new ConfigurationBuilder();
configBuilder.AddCommandLine(args);
configBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
var config = configBuilder.Build();
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IConfiguration>(p => config);
// 日志的框架
serviceCollection.AddLogging(builder =>
{
builder.AddConfiguration(config.GetSection("Logging"));// 注册 Logging 配置的 Section
builder.AddConsole();// 先使用一个 Console 的日志输出提供程序
builder.AddDebug();
});
我们可以观察到配置的框架和日志的框架,它们的设计模式是很相似的
区别就是:
配置的框架是从不同的数据源读取数据并且供给我们结构化的数据可以读取
日志框架是用统一的记录方式,让我们可以把日志记录到不同的地方去,输出到不同的地方去
接下来演示一下关于日志的作用域的部分
日志作用域几个常用场景:
1、一个事务包含多条操作时:比如说在一个事务里面去操作的时候,会需要记录多条日志,需要把多条日志串联在一起,而不是记录成一行
2、复杂流程的日志关联时:比如说工作流流程里面去进入这个日志
3、调用链追踪与请求处理过程对应时:如果在调用链追踪过程中记录了多条日志,希望把日志串联在一起的时候,作用域就发挥了作用
主程序
namespace LoggingScopeDemo
{
class Program
{
static void Main(string[] args)
{
var configBuilder = new ConfigurationBuilder();
configBuilder.AddCommandLine(args);
configBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
var config = configBuilder.Build();
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton(p => config); //用工厂模式将配置对象注册到容器管理
serviceCollection.AddLogging(builder =>
{
builder.AddConfiguration(config.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
});
IServiceProvider service = serviceCollection.BuildServiceProvider();
var logger = service.GetService<ILogger<Program>>();
// 相当于记录了一条上下文串联的日志
using (logger.BeginScope("ScopeId:{scopeId}", Guid.NewGuid()))
{
logger.LogInformation("这是Info");
logger.LogError("这是Error");
logger.LogTrace("这是Trace");
}
}
}
}
新增配置文件 appsettings.json,右键属性,如果较新则复制
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Information",
"LoggingScopeDemo.Program": "Trace",
"alogger": "Trace"
}
}
}
}
启动程序,输出如下:
info: LoggingScopeDemo.Program[0]
这是Info
fail: LoggingScopeDemo.Program[0]
这是Error
trce: LoggingScopeDemo.Program[0]
这是Trace
和之前一样的输出,接着修改配置文件
"IncludeScopes": true,
启动程序,输出如下:
info: LoggingScopeDemo.Program[0]
=> ScopeId:b8ef7682-6c6d-4f74-83c8-b9fd4613c623
这是Info
fail: LoggingScopeDemo.Program[0]
=> ScopeId:b8ef7682-6c6d-4f74-83c8-b9fd4613c623
这是Error
trce: LoggingScopeDemo.Program[0]
=> ScopeId:b8ef7682-6c6d-4f74-83c8-b9fd4613c623
这是Trace
可以看到,日志里面有 scope,并且三条日志都包含了相同的 ScopeId,这个是由我们决定 Scope 的内容是什么,一般推荐使用一个唯一标识,比如 HTTP 请求的 id,或者是 session 的 id,或者是事务的 id
接着修改为循环
// 只要输入不是 Esc 就循环执行
while (Console.ReadKey().Key != ConsoleKey.Escape)
{
// 相当于记录了一条上下文串联的日志
using (logger.BeginScope("ScopeId:{scopeId}", Guid.NewGuid()))
{
logger.LogInformation("这是Info");
logger.LogError("这是Error");
logger.LogTrace("这是Trace");
}
Console.WriteLine("============分割线=============");
}
Console.ReadKey();
启动程序,输出如下:
info: LoggingScopeDemo.Program[0]
=> ScopeId:cc25dd86-d3fe-41e8-b607-61912c65bde7
这是Info
============分割线=============
fail: LoggingScopeDemo.Program[0]
=> ScopeId:cc25dd86-d3fe-41e8-b607-61912c65bde7
这是Error
trce: LoggingScopeDemo.Program[0]
=> ScopeId:cc25dd86-d3fe-41e8-b607-61912c65bde7
这是Trace
这里可以看到分割线有点错乱,这是因为 Console 的提供程序实际上内部是用异步的方式在记录,那也就是这里遇到并发的问题
调整一下代码,让主线程休息一下
System.Threading.Thread.Sleep(100);
Console.WriteLine("============分割线=============");
这样子启动之后顺序就正确了
在程序启动的情况下,修改 Debug 目录下的配置文件
"IncludeScopes": false,
修改保存后在控制台输入回车,可以看到配置生效了,意味着可以使用配置热更新能力来动态修改配置的输出,调整配置输出的级别
比如将
"LoggingScopeDemo.Program": "Trace",
修改为
"LoggingScopeDemo.Program": "Error",
修改保存后在控制台输入回车,只会输出 Error 级别
GitHub源码链接:
这是在控制台里面的效果,接下来看一下在一个 ASP.NET Core Web 应用下面的日志是什么样子
这是一个默认的工程,仅仅在应用程序里面加了两行代码
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
_logger.LogInformation("开始Get了");
_logger.LogInformation("Get睡醒了");
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
日志级别
"Console": {
"IncludeScopes": true
}
启动程序,输出如下:
info: LoggingDemo.Controllers.WeatherForecastController[0]
=> RequestPath:/weatherforecast RequestId:0HLU2MTQ99HO2:00000001, SpanId:|7bb9cb12-4a0fe499cae27707., TraceId:7bb9cb12-4a0fe499cae27707, ParentId: => LoggingDemo.Controllers.WeatherForecastController.Get (LoggingDemo)
开始Get了
info: LoggingDemo.Controllers.WeatherForecastController[0]
=> RequestPath:/weatherforecast RequestId:0HLU2MTQ99HO2:00000001, SpanId:|7bb9cb12-4a0fe499cae27707., TraceId:7bb9cb12-4a0fe499cae27707, ParentId: => LoggingDemo.Controllers.WeatherForecastController.Get (LoggingDemo)
Get睡醒了
可以看到,记录的 开始Get了 以及 Get睡醒了,都包含了 RequestPath,RequestId,SpanId,TraceId 这些信息,这些信息是当前请求的上下文
也就意味着可以在记录日志的时候,用请求上下文把日志串联起来,多个请求的日志可以区分开来,无论记录了多条还是单条
也就意味着可以在事务处理的过程中,复杂的流程的过程中,或者调用链的处理过程中,当然还有其他的场景任意的需要将多条日志串联起来的场景,都可以用作用域来实现这个能力
GitHub源码链接:
https://github.com/MingsonZheng/DotNetCoreDevelopmentActualCombat/tree/main/LoggingDemo



本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
.NET Core开发实战(第19课:日志作用域:解决不同请求之间的日志干扰)--学习笔记的更多相关文章
- 2月送书福利:ASP.NET Core开发实战
大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...
- [ASP.NET Core开发实战]开篇词
前言 本系列课程文章主要是学习官方文档,再输出自己学习心得,希望对你有所帮助. 课程大纲 本系列课程主要分为三个部分:基础篇.实战篇和部署篇. 希望通过本系列课程,能让大家初步掌握使用ASP.NET ...
- [ASP.NET Core开发实战]基础篇02 依赖注入
ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...
- TensorFlow实战第七课(dropout解决overfitting)
Dropout 解决 overfitting overfitting也被称为过度学习,过度拟合.他是机器学习中常见的问题. 图中的黑色曲线是正常模型,绿色曲线就是overfitting模型.尽管绿色曲 ...
- zuul开发实战(限流,超时解决)
什么是网关 API Gateway,是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求.鉴权.监控.缓存.限流等功能 统一接入 * 智能路由 * AB测试.灰度测试 ...
- .NET Core开发实战(第11课:文件配置提供程序)--学习笔记
11 | 文件配置提供程序:自由选择配置的格式 文件配置提供程序 Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configu ...
- 2、SpringBoot接口Http协议开发实战8节课(1-6)
1.SpringBoot2.xHTTP请求配置讲解 简介:SpringBoot2.xHTTP请求注解讲解和简化注解配置技巧 1.@RestController and @RequestMapping是 ...
- [ASP.NET Core开发实战]基础篇03 中间件
什么是中间件 中间件是一种装配到应用管道,以处理请求和响应的组件.每个中间件: 选择是否将请求传递到管道中的下一个中间件. 可在管道中的下一个中间件前后执行. ASP.NET Core请求管道包含一系 ...
- [ASP.NET Core开发实战]基础篇01 Startup
Startup,顾名思义,就是启动类,用于配置ASP.NET Core应用的服务和请求管道. Startup有两个主要作用: 通过ConfigureServices方法配置应用的服务.服务是一个提供应 ...
- 2、SpringBoot接口Http协议开发实战8节课(7-8)
7.SpringBoot2.x文件上传实战 简介:讲解HTML页面文件上传和后端处理实战 1.讲解springboot文件上传 MultipartFile file,源自SpringMVC 1)静态页 ...
随机推荐
- nginx 负载均衡 proxy_pass 与 upstream 及 rewrite ,expires 的配置总结
本文为博主原创,转载请注明出处: 先查看 一段 nginx 相关的配置: location /test/ { set $arg_remote_addr $request_id; proxy_p ...
- zookeeper 集群环境搭建及集群选举及数据同步机制
本文为博主原创,未经允许不得转载: 目录: 1. 分别创建3个data目录用于存储各节点数据 2. 编写myid文件 3. 编写配置文件 4.分别启动 5.分别查看状态 6. 检查集群复制情况 ...
- 【荐】开源Winform控件库:花木兰控件库
微信好友推荐,挺好看的Winfrom控件库,下面来看看. 介绍 基于 C#(语言) 4.0 . VS2019 . Net Framework 4.0(不包括Net Framework 4.0 Clie ...
- SV 自定义数据类型
概述 自定义类型 枚举类型 定义枚举值 自定义枚举类型 枚举类型之间进行赋值是可以的 枚举类型可以赋值给整型,整型不能直接赋值给枚举类型 枚举类型 + 1 ==> 会进行隐式的转换,枚举类型转换 ...
- ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载AAC音频流
前言: RTSP,RTCP,RTP一般是一起使用,在FFmpeg和live555这些库中,它们为了更好的适用性,所以实现起来非常复杂,直接查看FFmpeg和Live555源代码来熟悉这些协议非常吃力, ...
- [转帖]Docker资源(CPU/内存/磁盘IO/GPU)限制与分配指南
https://zhuanlan.zhihu.com/p/417472115 什么是cgroup? cgroups其名称源自控制组群(control groups)的简写,是Linux内核的一个功能, ...
- [转帖]Kingbase实现Oracle userenv函数功能
目录 1. 问题 2. 文档概述 3. Oracle userenv()函数功能调研 3.1. 函数名称/函数原型 3.2. 函数功能 3.3. 参数介绍 3.3.1. Parameter 3.4. ...
- 【转帖】ESXI上安装和使用MegaCli
https://www.diewufeiyang.com/post/964.html 一.第一步获取安装文件,官网目前搜索也找不到了,这里附件提供之前保存的安装文件 点击下载 二.使用VMware v ...
- [转帖]修改vcenter数据库账号密码
1.修改sqlserver sa账号密码 2.停止vcenter服务 cd C:\Program Files\VMware\vCenter Server\bin service-control --l ...
- [转帖]Linux块层技术全面剖析-v0.1
Linux块层技术全面剖析-v0.1 perftrace@gmail.com 前言 网络上很多文章对块层的描述散乱在各个站点,而一些经典书籍由于更新不及时难免更不上最新的代码,例如关于块层的多队列.那 ...