前言

在项目的开发维护阶段,有时候我们关注的问题不仅仅在于功能的实现,甚至需要关注系统发布上线后遇到的问题能否及时的查找并解决。所以我们需要有一个好的解决方案来及时的定位错误的根源并做出正确及时的修复,这样才能不影响系统正常的运行状态。

这个时候我们发现,其实在asp.net core中已经内置了日志系统,并提供了各种内置和第三方日志记录提供程序的日志记录接口,在进行应用开发中,可以进行统一配置,并且利用第三方日志框架相结合,更加有效的实现日志记录。所以在这个系列中,主要是对内置日志记录系统的学习,以及后续使用第三方日志框架集成我们需要的日志系统。

说明

在这一篇中主要是对日志记录的配置进行说明,从开始配置日志,以及后续使用配置进行日志处理。

在新建项目成功之后,我们都会看到一个命名为appsettings.json配置,打开一看,短短的几行配置,

  "Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},

然后启动运行的时候,程序会在调试面板和控制台中分别输出显示来源如下:

在控制台中:

在调试面板中:

这里的日志配置,在系统中到底都起来什么作用?让我们来一探究竟吧!

开始

默认配置

我们查看源代码发现,在程序的入口点中发现,在初始化时候,通过CreateDefaultBuilder方法来实现日志记录的默认配置。

public class Program
{public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});
}

所以下面我们看一下CreateDefaultBuilder在源码中都对日志做了哪些默认配置?

        public static IHostBuilder CreateDefaultBuilder(string[] args){var builder = new HostBuilder();builder.UseContentRoot(Directory.GetCurrentDirectory());builder.ConfigureHostConfiguration(config =>{config.AddEnvironmentVariables(prefix: "DOTNET_");if (args != null){config.AddCommandLine(args);}});builder.ConfigureAppConfiguration((hostingContext, config) =>{var env = hostingContext.HostingEnvironment;config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)){var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));if (appAssembly != null){config.AddUserSecrets(appAssembly, optional: true);}}config.AddEnvironmentVariables();if (args != null){config.AddCommandLine(args);}}).ConfigureLogging((hostingContext, logging) =>{var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);if (isWindows){logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);} logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));logging.AddConsole();logging.AddDebug();logging.AddEventSourceLogger();if (isWindows){logging.AddEventLog();}}).UseDefaultServiceProvider((context, options) =>{var isDevelopment = context.HostingEnvironment.IsDevelopment();options.ValidateScopes = isDevelopment;options.ValidateOnBuild = isDevelopment;});return builder;}

通过上面这一段源码我们可以看到一个命名为ConfigureLogging的对象,我们根据命名的意思大致可以看出,这是一个配置日志的方法,继续查看ConfigureLogging源码

    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<HostBuilderContext, ILoggingBuilder> configureLogging){return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder)));}

通过IServiceCollection注册服务集合容器,将日志服务添加到这个服务容器,使用AddLogging方法实现对日志服务的注册。

    public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure){if (services == null){throw new ArgumentNullException(nameof(services));}services.AddOptions();services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>(new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));configure(new LoggingBuilder(services));return services;}

通过AddLogging添加到服务集合容器,先通过添加所需的配置AddOptions,通过注入的方式实现默认的ILoggerFactory,ILogger ( 这个会在后续的篇章中进行说明),再后通过LoggingBuilder完成日志对象的创建,

public interface ILoggingBuilder
{IServiceCollection Services { get; }
}
internal class LoggingBuilder : ILoggingBuilder
{public LoggingBuilder(IServiceCollection services){Services = services;}public IServiceCollection Services { get; }
}

对日志系统的配置,用于提供程序的接口,ILoggingBuilder后面可以对该对象进行拓展使用。

通过以上的流程CreateDefaultBuilder方法,实现对预先配置的默认值初始化,因此也发现了其中的ConfigureLogging也是其中要进行默认初始化的值,也就是系统默认的日志配置。

单独把ConfigureLogging这一块的源码拎出来再看看:

        .ConfigureLogging((hostingContext, logging) =>{var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);if (isWindows){logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);}logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));logging.AddConsole();logging.AddDebug();logging.AddEventSourceLogger();if (isWindows){logging.AddEventLog();}})

在asp.net core启动中,根据操作系统平台适应不同的服务,在windows服务中,将EventLogLoggerProvider的默认值设置为警告或者更高的级别。

AddConfiguration : 添加系统日志的全局配置。

在配置中,可以根据提供的不同类型程序来针对实现日志记录的输出方式。而这里默认实现的AddConsole()AddDebug() 和AddEventSourceLogger()分别是将日志输出到控制台、调试窗口中,以及提供写入事件源。

AddConsole : 添加控制台到工厂方法中,用来将日志记录到控制台中。

AddDebug : 添加Debug窗口到工厂方法中,用来将日志记录到窗口中。

说明:asp.net core 内置的日志接口中,实现了多种内置的日志提供器,除了上面默认实现的ConsoleDebugEventSource,还包括下面的这几个

EventLog :

TraceSource

AzureAppServicesFile

AzureAppServicesBlob

ApplicationInsights

还记得上面提到的appsettings.json配置吗?在这里,我们来看看

{"Logging": {"LogLevel": {"Default": "Debug","Microsoft": "Information"},"Console": {"LogLevel": {"Default": "Debug","System": "Warning"}}}
}

AddConfiguration中,

logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));

获取配置文件的Logging数据,实现全局配置,

        public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration){builder.AddConfiguration();builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration));builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration));builder.Services.AddSingleton(new LoggingConfiguration(configuration));return builder;}
internal class LoggerFilterConfigureOptions : IConfigureOptions<LoggerFilterOptions>
{private const string LogLevelKey = "LogLevel";private const string DefaultCategory = "Default";private readonly IConfiguration _configuration;public LoggerFilterConfigureOptions(IConfiguration configuration){_configuration = configuration;}public void Configure(LoggerFilterOptions options){LoadDefaultConfigValues(options);}private void LoadDefaultConfigValues(LoggerFilterOptions options){if (_configuration == null){return;}options.CaptureScopes = _configuration.GetValue(nameof(options.CaptureScopes), options.CaptureScopes);foreach (var configurationSection in _configuration.GetChildren()){if (configurationSection.Key.Equals(LogLevelKey, StringComparison.OrdinalIgnoreCase)){// Load global category defaultsLoadRules(options, configurationSection, null);}else{var logLevelSection = configurationSection.GetSection(LogLevelKey);if (logLevelSection != null){// Load logger specific rulesvar logger = configurationSection.Key;LoadRules(options, logLevelSection, logger);}}}}private void LoadRules(LoggerFilterOptions options, IConfigurationSection configurationSection, string logger){foreach (var p in configurationSection.AsEnumerable(true)){if (TryGetSwitch(p.Value, out var level)){var category = p.Key;if (category.Equals(DefaultCategory, StringComparison.OrdinalIgnoreCase)){category = null;}var newRule = new LoggerFilterRule(logger, category, level, null);options.Rules.Add(newRule);}}}
}

以上是AddConfiguration实现的整体流程源码,默认注册实现LoggerFilterConfigureOptions对配置数据的读取,其中定义的 LogLevelKey = "LogLevel"DefaultCategory = "Default" 默认字符串,以此来获取默认全局配置数据。

在默认配置的文本格式appsettings.json中,Logging属性可以具有LogLevel和日志提供程序属性。Logging 下的 LogLevel 属性指定了用于记录所选类别的最低级别。在本例中, Microsoft 类别在 Information 级别记录,其他均在 Debug 级别记录。

日志级别说明:每一个日志都有指定的日志级别值,日志级别判断指示严重性或重要性。使用日志等级可以很好的过滤想要的日志,记录日志记录问题的同时,甚至为我们提供非常详细的日志信息。

LogLevel 严重性:Trace < Debug < Information < Warning < Error < Critical < None。

日志级别 常用场景
Trace = 0 记录一些对程序员调试问题有帮助的信息, 其中可能包含一些敏感信息, 所以应该避免在 生产环境中启用Trace日志,因此不应该用于生产环境。默认应禁用。
Debug = 1 记录一些在开发和调试阶段有用的短时变 量(Short-term usefulness), 所以除非为了临时排除生产环境的 故障,开发人员应该尽量避免在生产环境中启用Debug日志,默认情况下这是最详细的日志。
Information = 2 记录跟踪应用程序的一些流程, 例如,记录当前api请求的url。
Warning = 3 记录应用程序中发生出现错误或其它导致程序停止的流程异常信息。这些信息中可能包含错误消息或者错误产生的条件, 可供后续调查,例如, 文件未找到
Error = 4 记录应用程序中某个操作产生的错误和异常信息。这些消息应该指明当前活动或操作(比如当前的 HTTP 请求),而不是应用程序范围的故障。
Critical = 5 记录一些需要立刻修复,急需被关注的问题,应当记录关键级别的日志。例如数据丢失,磁盘空间不足等。

日志级别只需要简单的通过 AddFilter 对日志的过滤级别配置一下就行了。同时也可以通过自定义在

Logging.{providername}.LogLevel 中指定了级别,则这些级别将重写 Logging.LogLevel 中设置的所有内容。(在下文自定义中说明)

由此可以看出,日志记录提供程序配置由一个或多个配置提供程序提供,如文件格式(系统自带的appsettings.json)或者通过(已安装或已创建的)自定义提供程序(下文会说明自定义方式)。

自定义配置

看完了上面实现的默认配置之后,我们也清楚了可以修改默认配置实现不同等级日志的输出,因此,我们也可以通过自定义的方式,对默认配置的修改,实现我们想要的日志记录方式。

可以通过自行选择添加提供程序来替换默认配置的提供的程序。这样就实现自定义。自定义的方式有很多,比如

1. 代码添加提供程序

调用ClearProviders,清除默认之后,可添加所需的提供程序。如下:

    public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args) //可以看出在使用模板创建项目的时候,默认添加了控制台和调试日志组件,并从appsettings.json中读取配置。.ConfigureLogging((hostingContext, logging) =>{logging.ClearProviders(); //去掉默认添加的日志提供程序//添加控制台输出logging.AddConsole();//添加调试输出logging.AddDebug();}).ConfigureWebHostDefaults(webBuilder =>{  webBuilder.UseStartup<Startup>();});
}

由上可以发现我们可以通过在入口程序中直接对添加ConfigureLogging(在上文中源码可以看出)拓展方法来实现我们的自定义配置。

2. 代码添加过滤器

过滤器AddFilter,添加过滤规则,可以为不同的日志提供者指定不同的过滤器,实现有效的自定义日志的输出。如下代码:

.ConfigureLogging(logging =>logging.AddFilter("System", LogLevel.Debug).AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace))

添加指定了全局的过滤器,作用于所有日志提供者,示例中的第二个 AddFilter 使用类型名称来指定调试提供程序。第一个 AddFilter 应用于全部提供程序,因为它未指定提供程序类型。

这里的AddFilter其实于之前读取配置文件信息添加配置AddConfiguration的作用相似,只是从配置文件的逻辑改成了以代码的方式实现过滤筛选,到最终也是对ConfigureOptions的配置。

3. 配置文件自定义

ASP.NET Core默认会从appSetting.json中的Logging属性读取日志的配置(当然你也可以从其他文件中读取配置),这里设置了不同的日志提供器产生的最低的日志级别,配置样例如下。

{"Logging": {"Debug": {"LogLevel": {"Default": "Information"}},"Console": {"LogLevel": {"Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning","Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug","Microsoft.AspNetCore.Mvc.Razor": "Error","Default": "Information"}},"LogLevel": {"Default": "Debug"}}
}

此 JSON 将创建 6 条筛选规则:Debug中1 条用于调试提供程序,Console中 4 条用于控制台提供程序,最后一条LogLevel 用于所有提供程序。创建 ILogger 对象时,为每个提供程序选择一个规则。

问题

虽然在这一节中只是对日志记录的配置进行了说明,但是在后续中也会对日志内部的核心运行机制进行说明介绍。所以,在这一篇中留下几个疑问

  1. 日志记录的输出可以在哪里查看?而又由什么实现决定的呢?

  2. 如何管理输出不同的日志呢?都有哪些方式呢?

以上的这些内容,会在下一篇进行介绍说明。

好了,今天的日志配置内容就说到这里了,希望能给大家在使用Core开发项目中对日志系统有进一步的认识。

总结

1. 本篇主要是对net core3.1中内置的系统日志进行配置使用,不管是基于默认配置的输出方式,还是自定义形式的配置,都是为了有效的输出日志记录,便于我们查找发现问题。

2.关于日志配置,其实都是在对ConfigureOptions的配置,只是在形式上是直接读取配置文件或通过代码的方式实现自定义来实现日志配置。

3. 后续会对内置的日志系统进一步说明,以及内部运行的主要核心机制。

4. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。

5. 推荐搜索关注公众号 --【DotNet技术谷】

基于.NetCore3.1系列 —— 日志记录之日志配置揭秘相关推荐

  1. 基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)

    一.前言 回顾:基于.NetCore3.1系列 -- 认证授权方案之授权揭秘 (上篇) 在上一篇中,主要讲解了授权在配置方面的源码,从添加授权配置开始,我们引入了需要的授权配置选项,而不同的授权要求构 ...

  2. 基于.NetCore3.1系列 —— 日志记录之初识Serilog

    前言 对内置日志系统的整体实现进行了介绍之后,可以通过使用内置记录器来实现日志的输出路径.而在实际项目开发中,使用第三方日志框架(如:Log4Net.NLog.Loggr.Serilog.Sentry ...

  3. 基于.NetCore3.1系列 —— 日志记录之自定义日志组件

    前言 回顾:日志记录之日志核心要素揭秘 在上一篇中,我们通过学习了解在.net core 中内置的日志记录中的几大核心要素,在日志工厂记录器(ILoggerFactory)中实现将日志记录提供器(IL ...

  4. 基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘

    前言 在上一篇中,我们已经了解了内置系统的默认配置和自定义配置的方式,在学习了配置的基础上,我们进一步的对日志在程序中是如何使用的深入了解学习.所以在这一篇中,主要是对日志记录的核心机制进行学习说明. ...

  5. python日志记录_Python日志记录

    python日志记录 To start, logging is a way of tracking events in a program when it runs and is in executi ...

  6. python日志文件保存在哪里,Python日志记录-检查日志文件的位置?

    What is the methodology for knowing where Python log statements are stored? i.e. if i do: import log ...

  7. 基于.NetCore3.1搭建项目系列 —— 认证授权方案之Swagger加锁

    1 开始 在之前的使用Swagger做Api文档中,我们已经使用Swagger进行开发接口文档,以及更加方便的使用.这一转换,让更多的接口可以以通俗易懂的方式展现给开发人员.而在后续的内容中,为了对a ...

  8. keil debug如何在watch直接修改变量值_python日志记录系列教程,内置logging模块(一),直接使用logging模块的基础日志记录

    前言:成熟的软件开发不可避免的要进行日志记录,python内置模块logging提供了强大的日志记录能力,本文将从多个角度,由浅入深的介绍logging的常见使用方法和一些基本概念,本此系列文章分为两 ...

  9. cmd长ping记录日志和时间_Gin 框架系列 — 路由中间件:日志记录

    概述 首先同步下项目概况: 上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 validator.v8 版本,现已更新到 validator.v9 版本,最新代码查看 github 即可. ...

最新文章

  1. matlab plot绘图函数官方详解
  2. 巧妙异或思路解子集问题,面试官叫绝
  3. centos 下使用sublime
  4. 面象对象设计6大原则之四:接口隔离原则
  5. 粗暴,干就完了----徐晓冬似的C语言自学笔记-----实现一个链表结构
  6. Valid Number 1
  7. OpenStack SFC 深入剖析
  8. html5发光动态效果图,一款带有发光动画的HTML5表单
  9. 软件测试 | APP测试 —— Appium 自动化测试框架的初识与学习要领
  10. php只保留两位小数
  11. 编译器错误~写JS还是谨慎点好
  12. date java format_java-DateFormat
  13. 华为u2020操作指南_用手机水平仪检测水平,操作简单,帮助甚大
  14. 汇编编写正弦函数代码
  15. 硬科技驱动西咸新区发展新引擎
  16. python 01列表异或_【快速跟水】图像隐写的python实现
  17. .bat 常用命令
  18. 疑似集体出走,支付宝蚂蚁庄园小鸡不见了
  19. 在word2015中的条形图在灰度的情况下不明显
  20. 《从菜鸟到大师》Python工程师之 Python语言基础 00

热门文章

  1. UIView 的基础
  2. PHP开发学习-Apache+PHP+MySQL环境搭建
  3. 失业日志:2009年10月12日星期一
  4. 如何在Word,Excel和PowerPoint 2010中裁剪图片
  5. 设备无法获得谷歌运行怎么办_因此,您刚刚获得了Google主页。 怎么办?
  6. 修复windows脸部识别_如何在Windows 10中改善面部识别
  7. mysql ldf文件太大_Linux_数据库清除日志文件(LDF文件过大),清除日志: 复制代码 代码如 - phpStudy...
  8. [译] 机器学习可以建模简单的数学函数吗?
  9. 循序渐进DB2(第2版)——DBA系统管理、运维与应用案例
  10. Linux安装 微信开发者工具(deepin linux ubt)