ASP.NET Core - 日志记录系统(二)

ASP.NET Core - 日志记录系统(二)

    • 2.4 日志提供程序
      • 2.4.1 内置日志提供程序
      • 2.4.2 源码解析

本篇接着上一篇 ASP.NET Core - 日志记录系统(一) 往下讲,所以目录不是从 1 开始的。

2.4 日志提供程序

2.4.1 内置日志提供程序

ASP.NET Core 包括以下日志记录提供程序作为共享框架的一部分:

  • Console
  • Debug
  • EventSource
  • EventLog

除此之外,还有一些微软官方提供的,但是没有和 .NET Core 框架集成的提供程序,如 ApplicationInsights 、AzureAppServicesFile 和 AzureAppServicesBlob ,这些在日常开发中使用的比较少,大家有兴趣的话可以自行了解一下。

  • 控制台

    Console 提供程序将输出记录到控制台。

    通用主机启动时就包含了控制台日志提供程序,使用它之后,我们记录的日志在调试过程中,可以在 VS 的 “调试输出” 窗口和 “ASP.NET Core Web 服务器” 窗口(非 IIS Press 启动) 看到。使用 dotnet run 运行应用时,可以在控制台窗口中看到。以 “Microsoft” 类别开头的日志来自 ASP.NET Core 框架代码。 ASP.NET Core 和应用程序代码使用相同的日志记录 API 和提供程序。

    控制台提供程序提供了多个 API,允许根据需要对输出格式、文字颜色等进行调整。

  • 调试

    Debug 提供程序使用 System.Diagnostics.Debug 类写入日志输出。 对 System.Diagnostics.Debug.WriteLine 的调用写入到 Debug 提供程序。

    在 Linux 上,Debug 提供程序日志位置取决于分发,并且可以是以下位置之一:

    • /var/log/message
    • /var/log/syslog
  • 事件来源

    EventSource 提供程序写入名称为 Microsoft-Extensions-Logging 的跨平台事件源。 在 Windows 上,提供程序使用的是 ETW。

    EventSource 日志提供程序记录的日志可以使用跨平台的 dotnet 追踪工具 dotnet-trace 来收集和跟踪。dotnet-trace 的安装和使用请参阅 dotnet-trace 诊断工具 - .NET CLI | Microsoft Learn 。

  • 事件日志

    仅在 Windows 系统下生效,可通过“事件查看器”进行日志查看。

    EventLog 提供程序将日志输出发送到 Windows 事件日志。 与其他提供程序不同,EventLog 提供程序不继承默认的非提供程序设置。 如果未指定 EventLog 日志设置,则它们默认为 LogLevel.Warning。若要记录低于 LogLevel.Warning 的事件,请显式设置日志级别。

    默认情况下,记录下来的事件日志一些基本参数如下:

    • LogName:“Application”
    • SourceName:“.NET Runtime”
    • MachineName:使用本地计算机名称。

    我们可以通过 AddEventLog 重载可以传入 EventLogSettings 来修改:

    var builder = WebApplication.CreateBuilder();
    builder.Logging.AddEventLog(eventLogSettings =>
    {eventLogSettings.SourceName = "MyLogs";
    });
    

上面已经讲到,在通过通用主机启动一个 .NET Core 应用时,会默认添加了 Console、Debug、EventSource 日志提供程序,如果运行平台是 Windows,还会添加 EventLog 日志提供程序。通过 ILoggingBuilder 我们可以重置并自定义日志提供程序的类型,这使得我们可以根据需要使用任何符合标准的日志提供程序。

如果是没有使用通用主机的非托管控制台应用,可以通过以下方式添加控制台日志提供程序:

using var loggerFactory = LoggerFactory.Create(builder =>
{builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Example log message");

不止控制台日志提供程序,其他各种日志提供程序都是按照日志记录系统框架进行开发和集成。显然,仅这些内置日志提供程序并不能满足我们生产开发中的使用,例如缺少最基础且常用的文件日志提供程序,还有在分布式应用已经非常普遍的业界现状下,时常需要将日志写分布式日志系统中进行统一的管理和分析,这些是微软没有提供的,但是第三方都有成熟的按照 .NET Core 日志记录系统体系架构开发的实现,这些将在下面细讲。

2.4.2 源码解析

.NET Core 日志记录系统的使用非常简单方便,其扩展性非常强,上面的章节已经讲解了基本的配置和使用,最核心的实现在于 LogeerFactoryILogger, LogeerFactory 用于日志系统的配置,ILogger 用于日志记录。下面从框架源码的角度,解析一下日志记录系统的实现。

阅读一个框架源码,我们可以从其开放出来的 API 作为入口,这样能较容易地梳理出其设计思路和实现脉络。首先是日志配置这一块,我们在应用集成的时候,对日志系统的配置都是基于 ILoggingBuilder 的,当然通过上面章节的内容,大家都已经知道在我们通过 ILoggingBuilder 进行配置之前,通用主机已经进行了一些配置。

ILoggingBuilder 的实现类其实就只是保存了容器,其他各种可用的方法都是扩展方法,都是往容器之中添加配置。

在这里插入图片描述
在添加日志系统默认配置的时候,可以看到显示调用了 AddLogging() 方法,之后就是像我们自己在实际应用中对日志系统进行配置一样帮我们添加了一些默认配置。
在这里插入图片描述
AddLogging() 方法是扩展方法,在 LoggingServiceCollectionExtensions 类中,在这个方法中往容器注入了三个日志记录系统最关键的东西,分别是 LoggerFactoryLogger<> 和日志配置。

在这里插入图片描述
当我们从使用日志记录器的时候,要么就是从容器中解析,要么就是通过 LoggerFactory.CreateLogger() 方法创建,查看 Logger<> 类的实现,其内部其实也是通过 LoggerFactory 创建了 ILogger 实例,注意这里的 ILogger 是没有泛型的,最终我们使用的其实都是这一个没有泛型的。

在这里插入图片描述
LoggerFactory 在其初始化的时候,会从容器中解析出我们添加的日志记录提供程序以及和日记记录系统相关的配置信息,并将日志记录提供程序保存到集合中。

在这里插入图片描述
当调用 CreateLogger 方法时,会创建 Logger 实例,为其配置并应用过滤规则,并将其保存起来。

在这里插入图片描述
在这里插入图片描述
这里就引出了三个重要的内部实现 LoggerLoggerInformationMessageLoggerLogger 是上面讲到的我们最终实际使用的 ILogger 的实现类,它的构造函数中需要传入 LoggerInformation 数组,LoggerInformation 数组与日志提供程序的数量对应。LoggerInformation 是一个结构体,是针对特定日志记录提供程序和日志类别的封装,在内部创建了特定于具体日志提供程序的日志记录器。

在这里插入图片描述
MessageLogger 是最终的日志信息书写的地方,它也是一个结构体,包含了规则过滤等内容,可以看到它的构造函数中传入了 LoggerInformationLogger 属性,也就是说最终也是使用特定于日志提供程序的日志记录器的。
在这里插入图片描述
最终返回的 MessageLogger 数组赋值给了 LoggerMessageLogger 数组不一定与日志记录提供程序的数量一样,因为有些日志记录提供程序在规则配置检查中可能跳过了。

Logger 类应用了装饰器模式,对多种日志记录提供程序的记录器进行了包装,提供了统一的日志记录 API,使得我们在使用时可以通过统一的入口将日志同时书写到不同的地方。当我们调用 Logger 实例的 Log 方法时,实际上是遍历了 MessageLogger 数组,通过具体的日志提供程序对应的日志记录器对当前日志级别进行检查,并进行最终的日志记录。

在这里插入图片描述

以控制台提供程序为例,这里中间有很多代码,其实只是为了实现更好的扩展性和性能,可以先忽略不看,最终也只是返回了特定 ConsoleLogger
在这里插入图片描述
ConsoleLogger 中的 Log 方法最终是将日志信息放到队列中,再由队列处理器写到控制台中。

在这里插入图片描述
了解完 .NET Core 日志记录系统的整体实现逻辑之后,我们想实现一个自己的日志提供程序其实还是比较简单的,当然如果要像微软内置的日志记录提供程序,或者第三方成熟的日志框架那样,各种细节处理得很好,就稍微有些难度了。以下是一个简单的示例,模仿默认日志记录提供程序的的实现方式,将日志记录到文件中:

(1) 创建一个类库项目,并引入以下依赖包

Install-Package Microsoft.Extensions.Logging.Abstractions
Install-Package Microsoft.Extensions.Logging

(2) 首先是实现 ILogger 接口,提供我们的日志记录器

internal sealed class WeWantFileLogger : ILogger
{private readonly object _sync = new object();/// <summary>/// 创建日志记录域/// </summary>/// <typeparam name="TState"></typeparam>/// <param name="state"></param>/// <returns></returns>/// <exception cref="NotImplementedException"></exception>public IDisposable? BeginScope<TState>(TState state) where TState : notnull{// 由于不准备支持日志记录域功能,这里返回一个空实现return NullScope.Instance;}/// <summary>/// 判断是否记录日志/// </summary>/// <param name="logLevel"></param>/// <returns></returns>public bool IsEnabled(LogLevel logLevel){return logLevel != LogLevel.None;}/// <summary>/// 记录日志,这里简单的演示例子/// 一个可用于正式环境的文件记录器还需考虑很多可扩展性、性能等因素/// </summary>/// <typeparam name="TState"></typeparam>/// <param name="logLevel"></param>/// <param name="eventId"></param>/// <param name="state"></param>/// <param name="exception"></param>/// <param name="formatter"></param>public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter){if (!IsEnabled(logLevel)){return;}ThrowHelper.ThrowIfNull(formatter);string message = formatter(state, exception);if(string.IsNullOrEmpty(message)){return;}message = $"{logLevel}: {message} { Environment.NewLine }";if(exception != null){message += Environment.NewLine + Environment.NewLine + exception;}var filePath = Path.Combine(Directory.GetCurrentDirectory(), "log.txt");lock (_sync){System.IO.File.AppendAllText(filePath, message);}}
}

其他相关的类如下:

internal sealed class NullScope : IDisposable
{public static NullScope Instance = new NullScope();private NullScope() { }public void Dispose(){}
}internal static partial class ThrowHelper
{/// <summary>Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is null.</summary>/// <param name="argument">The reference type argument to validate as non-null.</param>/// <param name="paramName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>internal static void ThrowIfNull(
#if NETCOREAPP3_0_OR_GREATER[NotNull]
#endifobject? argument,[CallerArgumentExpression("argument")] string? paramName = null){if (argument is null){Throw(paramName);}}#if NETCOREAPP3_0_OR_GREATER[DoesNotReturn]
#endifprivate static void Throw(string? paramName) => throw new ArgumentNullException(paramName);
}[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{public CallerArgumentExpressionAttribute(string parameterName){ParameterName = parameterName;}public string ParameterName { get; }
}

(3) 然后是实现 ILoggerProvider 接口,提供日志记录提供程序

[ProviderAlias("WeWantFile")]
public class WeWantFileLoggerProvider : ILoggerProvider
{public ILogger CreateLogger(string categoryName){return new WeWantFileLogger();}public void Dispose(){}
}

(4) 提供相应的扩展方法

public static class WeWantFileLoggerFactoryExtensions
{public static ILoggingBuilder AddWeWantFile(this ILoggingBuilder builder){builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, WeWantFileLoggerProvider>());return builder;}
}

(5) 在之前的项目中引用,并进行以下配置

// 清除默认的日志记录提供程序,添加自定义的日志记录提供程序
builder.Logging.ClearProviders();
builder.Logging.AddWeWantFile();

(6) 测试日志记录

调用之前测试用的 Web API 接口,代码如下:

// 各种日志API对应各种日志级别
// 断点
_logger.LogTrace("这是一个断点日志");
//调试
_logger.LogDebug("this is a debug.");
//信息
_logger.LogInformation("this is an info.");
//警告
_logger.LogWarning("this is a warning.");
//错误
_logger.LogError("this is an error.");
//当机
_logger.LogCritical("this is Critical");

可以看到项目文件夹中多了 log.txt文件,内容如下:

在这里插入图片描述
在这里插入图片描述
.NET Core 下的日志记录系统大概就介绍到这里,后面再继续介绍一下一些第三方日志框架,怎么将其集成到 .NET Core 框架中进行正式生产环境下的应用。



参考文章:

.NET Core 和 ASP.NET Core 中的日志记录 | Microsoft Learn
理解ASP.NET Core - 日志(Logging) - xiaoxiaotank - 博客园 (cnblogs.com)

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

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

相关文章

nginx的可视化配置工具nginxWebUI的使用

文章目录 1、nginx简介2、nginxWebUI2.1、技术解读2.2、开源版和专业版之间的区别2.3、功能解读 3、安装与使用3.1、下载镜像3.2、查看镜像3.3、启动容器3.4、使用 4、总结 1、nginx简介 Nginx 是一个高效的 HTTP 服务器和反向代理&#xff0c;它擅长处理静态资源、负载均衡和…

【C++】IO 流

文章目录 &#x1f449;C 语言的输入与输出&#x1f448;&#x1f449;流是什么&#x1f448;&#x1f449;C IO 流&#x1f448;C 标准 IO 流C 和 C 语言的输入格式问题C 的多次输入内置类型和自定义类型的转换日期的多次输入C 文件 IO 流文本文件和二进制文件的读写 &#x1…

基于springboot的幼儿园管理系统系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 项目包含&#xff1a; 完整源码数据库功能演示视频万字文档PPT 项目编码&#xff1…

Pycharm 使用教程

一、基本配置 1. 切换Python解释器 pycharm切换解释器版本 2. pycharm虚拟环境配置 虚拟环境的目的&#xff1a;创建适用于该项目的环境&#xff0c;与系统环境隔离&#xff0c;防止污染系统环境&#xff08;包括需要的库&#xff09;虚拟环境配置存放在项目根目录下的 ven…

Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)

文章目录 单例模式1️⃣特性&#x1f4aa;单例模式的类型与实现&#xff1a;类型懒汉式实现(线程不安全)懒汉式实现(线程安全&#xff09;双重锁校验懒汉式(线程安全)饿汉式实现(线程安全)使用类的内部类实现⭐枚举方式实现单例&#xff08;推荐&#xff09;&#x1f44d; 单例…

STM32 FreeRTOS中断管理

目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…

[Spring] SpringCloud概述与环境工程搭建

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

mobaxterm内置编辑器中文出现乱码如何解决:直接更换编辑器为本地编辑器

诸神缄默不语-个人CSDN博文目录 使用场景是我需要用mobaxterm通过SSH的方式登录服务器&#xff0c;进入服务器之后我就直接打开代码文件&#xff0c;mobaxterm会直接用内置的编辑器&#xff08;MobaTextEditor&#xff09;打开&#xff0c;但这会导致中文编程乱码。 我一开始是…

机器学习与人工智能的关系

机器学习与人工智能的关系 一、人工智能二、机器学习2.1 机器学习与人工智能的关系2.2 机器学习的本质 三、其他玩艺 曾几何时&#xff0c;人工智能还是个科幻名词&#xff0c;仿佛只属于未来世界。如今&#xff0c;它已经渗透到了我们生活的方方面面&#xff0c;成为顶流。我们…

一些常见的Java面试题及其答案

Java基础 1. Java中的基本数据类型有哪些&#xff1f; 答案&#xff1a;Java中的基本数据类型包括整数类型&#xff08;byte、short、int、long&#xff09;、浮点类型&#xff08;float、double&#xff09;、字符类型&#xff08;char&#xff09;和布尔类型&#xff08;boo…

构建高性能网络服务:从 Socket 原理到 Netty 应用实践

1. 引言 在 Java 网络编程中&#xff0c;Socket 是实现网络通信的基础&#xff08;可以查看我的上一篇博客&#xff09;。它封装了 TCP/IP 协议栈&#xff0c;提供了底层通信的核心能力。而 Netty 是在 Socket 和 NIO 的基础上&#xff0c;进一步封装的高性能、异步事件驱动的…

Docker PG流复制搭建实操

目录标题 制作镜像1. 删除旧的容器2. 创建并配置容器3. 初始化数据库并启动 主库配置参数4. 配置主库5. 修改 postgresql.conf 配置 备库配置参数6. 创建并配置备库容器7. 初始化备库 流复制8. 检查主库复制状态9. 检查备库配置 优化建议问题1&#xff1a;FATAL: using recover…

Elasticsearch 批量导入数据(_bluk方法)

官方API&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html 建议先看API POST /<索引名>/_bulk 格式要求&#xff1a; POST _bulk { "index" : { "_index" : "test", "_id" : &q…

Active Prompting with Chain-of-Thought for Large Language Models

题目 大型语言模型的思维链主动提示 论文地址&#xff1a;https://arxiv.org/abs/2302.12246 项目地址&#xff1a;https://github.com/shizhediao/active-prompt 摘要 大型语言模型(LLM)规模的不断扩大为各种需要推理的复杂任务带来了涌现能力&#xff0c;例如算术和常识推理…

Windows图形界面(GUI)-QT-C/C++ - QT控件创建管理初始化

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 控件创建 包含对应控件类型头文件 实例化控件类对象 控件设置 设置父控件 设置窗口标题 设置控件大小 设置控件坐标 设置文本颜色和背景颜色 控件排版 垂直布局 QVBoxLayout …

04、Redis深入数据结构

一、简单动态字符串SDS 无论是Redis中的key还是value&#xff0c;其基础数据类型都是字符串。如&#xff0c;Hash型value的field与value的类型&#xff0c;List型&#xff0c;Set型&#xff0c;ZSet型value的元素的类型等都是字符串。redis没有使用传统C中的字符串而是自定义了…

traceroute原理探究

文章中有截图&#xff0c;看不清的话&#xff0c;可以把浏览器显示比例放大到200%后观看。 linux下traceroute的原理 本文通过抓包观察一下linux下traceroute的原理 环境&#xff1a;一台嵌入式linux设备&#xff0c;内网ip是192.168.186.195&#xff0c;其上有192.168.202.…

无源器件-电容

电容器件的参数 基本概念由中学大学物理或电路分析内容获得&#xff0c;此处不做过多分析。 电容的产量占全球电子元器件产品的40%以上。 单位&#xff1a;法拉 F&#xff1b;1F10^6uF&#xff1b;电路中常见的104电容就是10*10^4pF100nF0.1uF C为电容&#xff0c;Rp为绝缘电…

自动连接校园网wifi脚本实践(自动网页认证)

目录 起因执行步骤分析校园网登录逻辑如何判断当前是否处于未登录状态&#xff1f; 书写代码打包设置开机自动启动 起因 我们一般通过远程控制的方式访问实验室电脑&#xff0c;但是最近实验室老是断电&#xff0c;但重启后也不会自动连接校园网账户认证&#xff0c;远程工具&…

知识图谱抽取分析中,如何做好实体对齐?

在知识图谱抽取分析中&#xff0c;实体对齐是将不同知识图谱中的相同实体映射到同一表示空间的关键步骤。为了做好实体对齐&#xff0c;可以参考以下方法和策略&#xff1a; 基于表示学习的方法&#xff1a; 使用知识图谱嵌入技术&#xff0c;如TransE、GCN等&#xff0c;将实体…