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. 《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能

    写程序最主要的目标就是使它在所有可能的情况下都正确工作.一个运行得很快但是给出错误结果的程序没有任何用处.程序员必须写出清晰简洁的代码,这样做不仅是为了自己能够看懂代码,也是为了在检査代码和今后需要修 ...

  2. Postman调试grpc

    转载请注明出处: 1.检查自己的postman是否支持 grpc,通过 File -> new -> ,出现如下图,则表示支持: 2.点击上图的grpc就会自动创建一个 grpc 的req ...

  3. Python-函数-算术函数

    #python-函数-算术函数 #(1)加减乘除 #加法 add(),减法 subtract(),乘法 multiply(),除法 divide() #作用:数组间的加减乘除 import numpy ...

  4. 本地打包编译jdk版本问题

    一.遇到的问题 进行项目接入pinpoint-ice插件,启动项目时pinpoint-agent日志报如下不支持jdk8的错误.'   二.排查 我们是在个人笔记本电脑上windows环境下通过mav ...

  5. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.20)

    百度网盘会员账号共享(11.20更新) 账号:zqv29268 密码:7003wayb 账号:rpz75364 密码:riyk1648 账号:0580quqn 密码:148hxhe 账号:132620 ...

  6. .NET集成IdGenerator生成分布式全局唯一ID

    前言 生成分布式唯一ID的方式有很多种如常见的有UUID.Snowflake(雪花算法).数据库自增ID.Redis等等,今天我们来讲讲.NET集成IdGenerator生成分布式全局唯一ID. 分布 ...

  7. [转帖]Oracle如何重启mmon/mmnl进程(AWR自动采集)

    https://www.cnblogs.com/jyzhao/p/10119854.html 学习一下 环境:Oracle 11.2.0.4 RAC现象:sysaux空间满导致无法正常生成快照,清理空 ...

  8. [转帖]必看!PostgreSQL参数优化

    https://zhuanlan.zhihu.com/p/333201734 前不久,一个朋友所在的公司,业务人员整天都喊慢. 朋友是搞开发的,不是很懂DB,他说他们应用的其实没什么问题,但是就是每天 ...

  9. 【转帖】Linux性能优化(十四)——CPU Cache

    一.CPU Cache 1.CPU Cache简介 CPU Cache是位于CPU与内存之间的临时存储器,容量比内存小但交换速度却比内存要快得多.Cache的出现主要是为了解决CPU运算速度与内存读写 ...

  10. 容器方式运行Mysql8.0.26的方法

    容器化运行Mysql8.0.26测试环境的方法 1. 前言 之前为了好处理,都是二进制包的方式安装mysql,但是有时候需要下载和安装也比较费时费力, 今天中午在弄Oracle RAC时想着以后能够容 ...