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 配置的 Sectionbuilder.AddConsole();// 先使用一个 Console 的日志输出提供程序builder.AddDebug();
});

我们可以观察到配置的框架和日志的框架,它们的设计模式是很相似的

区别就是:

配置的框架是从不同的数据源读取数据并且供给我们结构化的数据可以读取

日志框架是用统一的记录方式,让我们可以把日志记录到不同的地方去,输出到不同的地方去

接下来演示一下关于日志的作用域的部分

日志作用域几个常用场景:

1、一个事务包含多条操作时:比如说在一个事务里面去操作的时候,会需要记录多条日志,需要把多条日志串联在一起,而不是记录成一行

2、复杂流程的日志关联时:比如说工作流流程里面去进入这个日志

3、调用链追踪与请求处理过程对应时:如果在调用链追踪过程中记录了多条日志,希望把日志串联在一起的时候,作用域就发挥了作用

源码链接:
https://github.com/witskeeper/geektime/tree/master/samples/LoggingScopeDemo

主程序

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");}}}
}

配置文件

{"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 级别

这是在控制台里面的效果,接下来看一下在一个 ASP.NET Core Web 应用下面的日志是什么样子

源码链接:
https://github.com/witskeeper/geektime/tree/master/samples/LoggingDemo

这是一个默认的工程,仅仅在应用程序里面加了两行代码

[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 这些信息,这些信息是当前请求的上下文

也就意味着可以在记录日志的时候,用请求上下文把日志串联起来,多个请求的日志可以区分开来,无论记录了多条还是单条

也就意味着可以在事务处理的过程中,复杂的流程的过程中,或者调用链的处理过程中,当然还有其他的场景任意的需要将多条日志串联起来的场景,都可以用作用域来实现这个能力

.NET Core开发实战(第19课:日志作用域:解决不同请求之间的日志干扰)--学习笔记...相关推荐

  1. 《物联网开发实战》09 边缘中心:物联网网关有多重要?(学习笔记)

    仅作为本人学习<物联网开发实战>的学习笔记,原课程链接:极客时间<物联网开发实战>--郭朝斌 不是所有的设备都使用 Wi-Fi 这类可以直接接入互联网从而与云平台通信的通信技术 ...

  2. .NET Core开发实战(第5课:依赖注入:良好架构的起点)--学习笔记(上)

    05 | 依赖注入:良好架构的起点 为什么要使用依赖注入框架 借助依赖注入框架,我们可以轻松管理类之间的依赖,帮助我们在构建应用时遵循设计原则,确保代码的可维护性和可扩展性 ASP.NET Core ...

  3. .NET Core开发实战(定义API的最佳实践)Source Generators版

    前言 极客时间上的<.NET Core开发实战>是一门非常好的课程,作者肖伟宇在第31课(https://time.geekbang.org/course/detail/100044601 ...

  4. .NET Core开发实战(第18课:日志框架:聊聊记日志的最佳姿势)--学习笔记(下)...

    18 | 日志框架:聊聊记日志的最佳姿势 除了使用 CreateLogger 指定 logger 的名称,实际上还可以借助容器来构造 logger,通常情况下我们会定义自己的类 namespace L ...

  5. .NET Core开发实战(第18课:日志框架:聊聊记日志的最佳姿势)--学习笔记(上)...

    18 | 日志框架:聊聊记日志的最佳姿势 源码链接: https://github.com/witskeeper/geektime/tree/master/samples/LoggingSimpleD ...

  6. .NET Core开发实战(第22课:异常处理中间件:区分真异常与逻辑异常)--学习笔记(上)...

    22 | 异常处理中间件:区分真异常与逻辑异常 这一节我们来讲解一下错误处理的最佳实践 系统里面异常处理,ASP.NET Core 提供了四种方式 1.异常处理页 2.异常处理匿名委托方法 3.IEx ...

  7. .NET Core开发实战(第33课:集成事件:使用RabbitMQ来实现EventBus)--学习笔记(上)...

    33 | 集成事件:使用RabbitMQ来实现EventBus 这一节我们来讲解如何通过 CAP 组件和 RabbitMQ 来实现 EventBus 要实现 EventBus,我们这里借助了 Rabb ...

  8. 笔记合并_.NET Core开发实战(第23课:静态文件中间件:前后端分离开发合并部署骚操作)学习笔记(上)...

    23 | 静态文件中间件:前后端分离开发合并部署骚操作 我们先来看一下静态文件中间件有哪些能力 1.支持指定相对路径 2.支持目录的浏览 3.支持设置默认文档 4.支持多目录映射 源码链接: http ...

  9. .NET Core开发实战(第32课:集成事件:解决跨微服务的最终一致性)--学习笔记...

    32 | 集成事件:解决跨微服务的最终一致性 首先看一下集成事件的工作原理 它的目的时为了实现系统的集成,它主要是用于系统里面多个微服务之间相互传递事件 集成事件的实现方式有两种,一种是图上显示的发布 ...

最新文章

  1. qt-designer使用教程1--HelloWorld
  2. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录
  3. 2017年10月07日普及组 蚂蚁
  4. XScuGic_Connect分析
  5. 云原生时代|分布式系统设计知识图谱(内含22个知识点)
  6. 使用Oracle VM VirtualBox创建的虚拟机与本地电脑(物理机)搭建局域网
  7. 解决EasyDSS、EasyNVR流媒体RTMP、HLS(m3u8)、HTTP-FLV播放提示H5播放错误的问题
  8. 群体智能优化算法之烟花算法(Fireworks Algorithm,FWA)
  9. JVisualVM的使用教程
  10. 协议森林1——小喇叭开始广播:以太网与WiFi
  11. SpringCloud服务器服务注册在Euraka上,访问IP地址问题
  12. 呼叫中心点击拨打接口升级代码
  13. python判断邮箱格式是否正确_python:校验邮箱格式
  14. vim的关键词的补全功能
  15. 安卓签名文件与MD5 SHA1 SHA256
  16. PHP匿名在线聊天室系统源码
  17. springboot2.7.X+es8 集成开发
  18. sgu-207 Robbers
  19. 计算机网络笔记17 分类的IP地址
  20. 传奇脚本显示服务器开区时间代码,GOM引擎活动页面脚本,传奇私服服务端添加开即时真假半兽人脚本...

热门文章

  1. 多比Web 3D展示(3D机房/3D监控)中间件多比Web 3D展示(3D机房/3D监控)中间件免费下载购买地址...
  2. Codeforces Round #174 (Div. 2) Cows and Primitive Roots(数论)
  3. 原创:PHP内核研究:HASH表和变量
  4. vue-typescript
  5. 大厂高级前端面试题答案
  6. 《A Seat at the Table》作者访谈录
  7. 【 Tomcat 】tomcat8.0 调优配置
  8. 莫名其妙的主机名 VM_32_234_centos
  9. Ubuntu 12.04 x64 下安装 GStreamer+FFmpeg+Opencv
  10. Linux锁定和解锁用户