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源码链接:

https://github.com/MINGSON666/Personal-Learning-Library/tree/main/DotNetCoreDevelopmentActualCombat/LoggingScopeDemo

这是在控制台里面的效果,接下来看一下在一个 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课:日志作用域:解决不同请求之间的日志干扰)--学习笔记的更多相关文章

  1. 2月送书福利:ASP.NET Core开发实战

    大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...

  2. [ASP.NET Core开发实战]开篇词

    前言 本系列课程文章主要是学习官方文档,再输出自己学习心得,希望对你有所帮助. 课程大纲 本系列课程主要分为三个部分:基础篇.实战篇和部署篇. 希望通过本系列课程,能让大家初步掌握使用ASP.NET ...

  3. [ASP.NET Core开发实战]基础篇02 依赖注入

    ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...

  4. TensorFlow实战第七课(dropout解决overfitting)

    Dropout 解决 overfitting overfitting也被称为过度学习,过度拟合.他是机器学习中常见的问题. 图中的黑色曲线是正常模型,绿色曲线就是overfitting模型.尽管绿色曲 ...

  5. zuul开发实战(限流,超时解决)

    什么是网关 API Gateway,是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求.鉴权.监控.缓存.限流等功能 统一接入 * 智能路由 * AB测试.灰度测试 ...

  6. .NET Core开发实战(第11课:文件配置提供程序)--学习笔记

    11 | 文件配置提供程序:自由选择配置的格式 文件配置提供程序 Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configu ...

  7. 2、SpringBoot接口Http协议开发实战8节课(1-6)

    1.SpringBoot2.xHTTP请求配置讲解 简介:SpringBoot2.xHTTP请求注解讲解和简化注解配置技巧 1.@RestController and @RequestMapping是 ...

  8. [ASP.NET Core开发实战]基础篇03 中间件

    什么是中间件 中间件是一种装配到应用管道,以处理请求和响应的组件.每个中间件: 选择是否将请求传递到管道中的下一个中间件. 可在管道中的下一个中间件前后执行. ASP.NET Core请求管道包含一系 ...

  9. [ASP.NET Core开发实战]基础篇01 Startup

    Startup,顾名思义,就是启动类,用于配置ASP.NET Core应用的服务和请求管道. Startup有两个主要作用: 通过ConfigureServices方法配置应用的服务.服务是一个提供应 ...

  10. 2、SpringBoot接口Http协议开发实战8节课(7-8)

    7.SpringBoot2.x文件上传实战 简介:讲解HTML页面文件上传和后端处理实战 1.讲解springboot文件上传 MultipartFile file,源自SpringMVC 1)静态页 ...

随机推荐

  1. VUE里使用iframe在更改了src之后对应的网页并未刷新解决方案

    在vue 里使用iframe,在更新src后页面并未刷新, 在更改iframe src属性值之前加上这一句即可 document.getElementById(iframe的id).contentWi ...

  2. 全流程机器视觉工程开发(二)PaddleDetection:拉框,然后开始训练模型

    前言 我现在在准备做一个全流程的机器视觉的工程,之前做了很多理论相关的工作.大概理解了机器视觉的原理,然后大概了解了一下,我发现现在的库其实已经很发展了,完全不需要用到非常多的理论,只需要知道开发过程 ...

  3. spring boot 集成配置阿里 Druid监控配置

    本文为博主原创,转载请注明出处: github 地址如下:https://github.com/alibaba/druid/wiki 其相关问题也可参考:https://github.com/alib ...

  4. Java基础篇(05):函数式编程概念和应用

    目录 一.函数式概念 二.函数与方法 三.JDK函数基础 1.Lambda表达式 2.函数式接口 四.Optional类 1.Null判断 2.Optional应用 五.Stream流 一.函数式概念 ...

  5. vocode-markdown导出pdf插件

    1. 背景 在vocode中编辑markdown格式文本文件,并将其导出为pdf格式文件 2. 插件 在 vscode的扩展市场中搜索插件"Markdown PDF" 并安装 选中 ...

  6. Redis-逻辑库-select

  7. 【转帖】MySQL 8.0.32如期而至

    MySQL 8.0版本计划 MySQL 8.0开始采用快速迭代开发模式,基本上是每隔3个月就发布一个新的小版本.去年1月18日(2022.1.18)发布MySQL 8.0.28,今年1月17日发布My ...

  8. [转贴]30 分钟学会 AWK

    30 分钟学会 AWK https://mp.weixin.qq.com/s/X0ire4dYiceC2CzPU6JsSw? Linux爱好者 2017-01-08   (点击上方公众号,可快速关注) ...

  9. 记windows自定义bat脚本自启动

    自定义 Windows 启动脚本简化版 在本指南中,我们将使用一个简化的批处理文件(.bat)来演示如何创建自定义的 Windows 启动脚本.以下是一个基本的模板,您只需根据需要在 :begin 部 ...

  10. axios文件上传和 Content-Type类型介绍

    Content-Type的作用是什么? Content-Type: 用于在请求头部指定资源的类型和字符编码. 请求头中的content-type,就是 B端发给S端的数据类型描述 . 即告诉服务器端, ...