ASP.NET Core 6框架揭秘实例演示[15]:针对控制台的日志输出
针对控制台的ILogger实现类型为ConsoleLogger,对应的ILoggerProvider实现类型为ConsoleLoggerProvider,这两个类型都定义在 NuGet包“Microsoft.Extensions.Logging.Console”中。ConsoleLogger要将一条日志输出到控制台上,首选要解决的是格式化的问题,具体来说是如何将日志消息的内容荷载和元数据(类别、等级和事件ID等)格式化成呈现在控制台上的文本。针对日志的格式化由ConsoleFormatter对象来完成。(本篇提供的实例已经汇总到《ASP.NET Core 6框架揭秘-实例演示版》)
[S901]SimpleConsoleFormatter格式化器(源代码)
[S902]SystemdConsoleFormatter格式化器(源代码)
[S903]JsonConsoleFormatter格式化器(源代码)
[S904]改变ConsoleLogger的标准输出和错误输出(源代码)
[S905]自定义控制台日志的格式化器(源代码)
[S901]SimpleConsoleFormatter格式化器
下面了代码演示了如何使用SimpleConsoleFormatter来格式化控制台输出的日志。我们利用命令行参数控制是否采用单行文本输出和着色方案。在调用ILoggingBuiler接口的AddConsole扩展方法对ConsoleLoggerProvider对象进行注册之后,我们接着调用它的AddSimpleConsole扩展方法将SimpleConsoleFormatter作为格式化器,并利用作为参数的Action<SimpleConsoleFormatterOptions>委托根据命令行参数的对SimpleConsoleFormatterOptions配置选项进行了相应设置(S901)。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console; var configuration = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
var singleLine = configuration.GetSection("singleLine").Get<bool>();
var colorBebavior = configuration.GetSection("color").Get<LoggerColorBehavior>(); var logger = LoggerFactory.Create(builder => builder
.AddConsole()
.AddSimpleConsole(options =>
{
options.SingleLine = singleLine;
options.ColorBehavior = colorBebavior;
}))
.CreateLogger<Program>();
var levels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
levels = levels.Where(it => it != LogLevel.None).ToArray();
var eventId = 1;
Array.ForEach(levels,
level => logger.Log(level, eventId++, "This is a/an {0} log message.", level));
Console.Read();
我们已命名行的方式三次启动演示程序,并利用参数(singleLine=true, color=Disabled)控制对应的格式化行为。从图1所示的结果可以看出日志输出的格式是与我们指定的命名行参数是匹配的。
图1 基于SimpleConsoleFormatter的格式化
[S902]SystemdConsoleFormatter格式化器
我们使用如下这个实例来演示针对SystemdConsoleFormatter的格式化。如代码片段所示,我们利用命令行参数“includeScopes”来决定是否支持日志范围。在调用ILoggingBuilder接口的AddConsole扩展方法ConsoleLoggerProvider对象进行注册之后,我们接着调用了该接口的AddSystemdConsole扩展方法对SystemdConsoleFormatter及其配置选项进行注册。为了输出所有等级的日志,我们将最低日志等级设置为Trace。为了体现针对异常信息的输出,我们在调用Log方法是传入了一个Exception对象。
using Microsoft.Extensions.Logging; var includeScopes = args.Contains("includeScopes");
var logger = LoggerFactory.Create(builder => builder
.SetMinimumLevel(LogLevel.Trace)
.AddConsole()
.AddSystemdConsole(options => options.IncludeScopes = includeScopes))
.CreateLogger<Program>();
var levels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
levels = levels.Where(it => it != LogLevel.None).ToArray();
var eventId = 1;
Array.ForEach(levels, Log);
Console.Read(); void Log(LogLevel logLevel)
{
using (logger.BeginScope("Foo"))
{
using (logger.BeginScope("Bar"))
{
using (logger.BeginScope("Baz"))
{
logger.Log(logLevel, eventId++, new Exception("Error..."), "This is a/an {0} log message.", logLevel);
}
}
}
}
我们采用命令行的方式两次启动演示程序,第一次采用默认配置,第二次利用命令行参数“includeScopes”开启针对日志范围的支持。从图2所示的输出结果可以看出六条日志均以单条文本的形式输出到控制台上,对应的日志等级(Trace、Debug、Information、Warning、Error和Critical)均被转换成Syslog日志等级(7、7、6、4、3和2)。
图2 基于SystemdConsoleFormatter的格式化
[S903]JsonConsoleFormatter格式化器
我们对上面的演示程序略加改动,将针对ILoggingBuilder接口的AddSystemdConsole扩展方法的调用替换成调用AddJsonConsole扩展方法,后者帮助我们完成针对JsonConsoleFormatter及其配置选项的注册。为了减少控制台上输出的内容,我们移除了针对最低日志等级的设置。
using Microsoft.Extensions.Logging; var includeScopes = args.Contains("includeScopes"); var logger = LoggerFactory
.Create(builder => builder
.AddConsole()
.AddJsonConsole(options => options.IncludeScopes = includeScopes))
.CreateLogger<Program>();
var levels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
levels = levels.Where(it => it != LogLevel.None).ToArray();
var eventId = 1;
Array.ForEach(levels, Log); Console.Read(); void Log(LogLevel logLevel)
{
using (logger.BeginScope("Foo"))
{
using (logger.BeginScope("Bar"))
{
using (logger.BeginScope("Baz"))
{
logger.Log(logLevel, eventId++, new Exception("Error..."), "This is a/an {0} log message.", logLevel);
}
}
}
}
我们依然采用上面的方式两次执行改动后的程序。在默认以及开启日志范围的情况下,控制台分别具有图3所示的输出。可以看出输出的内容不仅包含参数填充生成完整内容,还包含原始的模板。日志范围的路径是以数组的方式输出的。
图3 基于JsonConsoleFormatter的格式化
[S904]改变ConsoleLogger的标准输出和错误输出
ConsoleLogger具有“标准输出”和“错误输出”两个输出渠道,分别对应着Console类型的静态属性Out和Error返回的TextWriter对象。对于不高于LogToStandardErrorThreshold设定等级的日志会采用标准输出,高于或者等于该等级的日志则采用错误输出。由于LogToStandardErrorThreshold属性的默认值为None,所以任何等级的日志都被写入标准输出。如下的代码片段演示了如何通过设置这个属性改变不同等级日志的输出渠道。
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console; using (var @out = new StreamWriter("out.log") { AutoFlush = true })
using (var error = new StreamWriter("error.log") { AutoFlush = true })
{
Console.SetOut(@out);
Console.SetError(error); var logger = LoggerFactory.Create(builder => builder
.SetMinimumLevel(LogLevel.Trace)
.AddConsole(options =>options.LogToStandardErrorThreshold = LogLevel.Error)
.AddSimpleConsole(options =>options.ColorBehavior = LoggerColorBehavior.Disabled))
.CreateLogger<Program>(); var levels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
levels = levels.Where(it => it != LogLevel.None).ToArray();
var eventId = 1;
Array.ForEach(levels, Log);
Console.Read(); void Log(LogLevel logLevel) => logger.Log(logLevel, eventId++, "This is a/an {0} log message.", logLevel);
}
如上面的代码片段所示,我们创建了两个针对本地文件(out.log和error.log)的StreamWriter,并通过调用Console类型的静态方法SetOut和SetError将其设置为控制台的标准输出和错误输出。在针对ILogingBuilder接口的AddConsole扩展方法调用中,我们将配置选项ConsoleLoggerOptions的LogToStandardErrorThreshold属性设置为Error,并调用SetMinimumLevel方法将最低日志等级设置为Trace。我们还调用AddSimpleConsole扩展方法禁用作色方案。当程序运行之后,针对具有不同等级的六条日志,四条不高于Error的日志被输出到如图4所示的out.log中,另外两条则作为错误日志被输出到error.log中,控制台上将不会有任何输出内容。
图4 标准输入和错误输出
[S905]自定义控制台日志的格式化器
为了能够更加灵活地控制日志在控制台上的输出格式,我们自定义了如下这个格式化器类型。如代码片段所示,这个名为TemplatedConsoleFormatter会按照指定的模板来格式化输出的日志内容,它使用的配置选项类型为TemplatedConsoleFormatterOptions,日志格式模板就体现在它的Template属性上。
public class TemplatedConsoleFormatter : ConsoleFormatter
{
private readonly bool _includeScopes;
private readonly string _tempalte; public TemplatedConsoleFormatter(IOptions<TemplatedConsoleFormatterOptions> options)
: base("templated")
{
_includeScopes = options.Value.IncludeScopes;
_tempalte = options.Value?.Template?? "[{LogLevel}]{Category}/{EventId}:{Message}\n{Scopes}\n";
} public override void Write<TState>(in LogEntry<TState> logEntry,
IExternalScopeProvider scopeProvider, TextWriter textWriter)
{
var builder = new StringBuilder(_tempalte);
builder.Replace("{Category}", logEntry.Category);
builder.Replace("{EventId}", logEntry.EventId.ToString());
builder.Replace("{LogLevel}", logEntry.LogLevel.ToString());
builder.Replace("{Message}", logEntry.Formatter?.Invoke(logEntry.State, logEntry.Exception)); if (_includeScopes && scopeProvider != null)
{
var buidler2 = new StringBuilder();
var writer = new StringWriter(buidler2);
scopeProvider.ForEachScope(WriteScope, writer); void WriteScope(object? scope, StringWriter state)
{
writer.Write("=>" + scope);
} builder.Replace("{Scopes}", buidler2.ToString());
}
textWriter.Write(builder);
}
} public class TemplatedConsoleFormatterOptions: ConsoleFormatterOptions
{
public string? Template { get; set; }
}
我们将TemplatedConsoleFormatter的别名指定为“templated”,格式模板利用占位符“{Category}”、“{EventId}”、“{LogLevel}”、“{Message}”和“{Scopes}”表示日志类别、事件ID、等级、消息和范围信息。 “[{LogLevel}]{Category}/{EventId}:{Message}\n{Scopes}\n”是我们提供的默认模板。现在我们采用如下这个名为appsettings.json的JSON文件来提供所有的配置。
{
"Logging": {
"Console": {
"FormatterName": "templated",
"LogToStandardErrorThreshold": "Error",
"FormatterOptions": {
"IncludeScopes": true,
"UseUtcTimestamp": true,
"Template": "[{LogLevel}]{Category}:{Message}\n"
}
}
}
}
如上面的代码片段所示,我们将控制台日志输出的相关设置定义在“Logging:Console”配置节中,并定义了格式化器名称(“templated”)、错误日志最低等级(“Error”)。“FormatterOptions”配置节的内容将最终绑定到TemplatedConsoleFormatterOptions配置选项上。在如下所示的演示程序中,我们加载这个配置文件并提取代表“Logging”配置节的IConfigguration对象,我们将这个对象作为参数调用ILoggingBuilder接口的AddConfiguration扩展方法进行了注册。
using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; var logger = LoggerFactory.Create(Configure).CreateLogger<Program>(); var levels = (LogLevel[])Enum.GetValues(typeof(LogLevel));
levels = levels.Where(it => it != LogLevel.None).ToArray();
var eventId = 1;
Array.ForEach(levels, Log);
Console.Read(); void Configure(ILoggingBuilder builder)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build()
.GetSection("Logging");
builder
.AddConfiguration(configuration)
.AddConsole()
.AddConsoleFormatter<TemplatedConsoleFormatter,TemplatedConsoleFormatterOptions>();
} void Log(LogLevel logLevel) => logger.Log(logLevel, eventId++, "This is a/an {0} log message.", logLevel);
在调用ILoggingBuilder接口的AddConsole扩展方法对ConsoleLoggerProvider进行注册之后,我们调用了它的AddConsoleFormatter<TemplatedConsoleFormatter,TemplatedConsoleFormatterOptions>方法,该方法将利用配置来绑定注册格式化器对应的TemplatedConsoleFormatterOptions配置选项。演示程序启动之后,每一条日志将按照配置中提供的模板进行格式化,并以图5所示的形式输出到控制台上。
图5 基于TemplatedConsoleFormatter的格式化
ASP.NET Core 6框架揭秘实例演示[15]:针对控制台的日志输出的更多相关文章
- ASP.NET Core 6框架揭秘实例演示[07]:文件系统
ASP.NET Core应用具有很多读取文件的场景,如读取配置文件.静态Web资源文件(如CSS.JavaScript和图片文件等).MVC应用的视图文件,以及直接编译到程序集中的内嵌资源文件.这些文 ...
- ASP.NET Core 6框架揭秘实例演示[08]:配置的基本编程模式
.NET的配置支持多样化的数据源,我们可以采用内存的变量.环境变量.命令行参数.以及各种格式的配置文件作为配置的数据来源.在对配置系统进行系统介绍之前,我们通过几个简单的实例演示一下如何将具有不同来源 ...
- ASP.NET Core 6框架揭秘实例演示[09]:配置绑定
我们倾向于将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置,我们将这个转换过程称为配置绑定.除了将配置树叶子节点配置节的绑定为某种标量对象外,我们还可以直接将一个配置 ...
- ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式
依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...
- ASP.NET Core 6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式
在整个软件开发维护生命周期内,最难的不是如何将软件系统开发出来,而是在系统上线之后及时解决遇到的问题.一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根 ...
- ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法
一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根据当前的运行状态预知未来可能发生的问题,并将问题扼杀在摇篮中.诊断跟踪能够帮助我们有效地纠错和排错&l ...
- ASP.NET Core 6框架揭秘实例演示[13]:日志的基本编程模式[上篇]
<诊断跟踪的几种基本编程方式>介绍了四种常用的诊断日志框架.其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net.NLog和Serilog 等.虽然这些框 ...
- ASP.NET Core 6框架揭秘实例演示[14]:日志的进阶用法
为了对各种日志框架进行整合,微软创建了一个用来提供统一的日志编程模式的日志框架.<日志的基本编程模式>以实例演示的方式介绍了日志的基本编程模式,现在我们来补充几种"进阶" ...
- ASP.NET Core 6框架揭秘实例演示[16]:内存缓存与分布式缓存的使用
.NET提供了两个独立的缓存框架,一个是针对本地内存的缓存,另一个是针对分布式存储的缓存.前者可以在不经过序列化的情况下直接将对象存储在应用程序进程的内存中,后者则需要将对象序列化成字节数组并存储到一 ...
随机推荐
- For Update 加锁分析
MySQL InnoDB 锁 - For Update 加锁分析: 1. InnoDB锁 简单介绍 2. 当前读加锁分析:REPEATABLE-READ 可重复读.READ-COMMITTED 读已提 ...
- python使用制表符或者换行符来添加空白--3
#!/usr/bin/python #coding=utf-8 message="python" print(message) print("\tpython" ...
- liunx查看哪个ip连接最多
[root@centos6 /]# netstat -na|grep ESTABLISHED|awk '{print $5}'|awk -F: '{print $1}'|sort|uniq -c|so ...
- redis与集群实用操作笔记
redis哨兵 部署方式 redis配置 首先需要区分的是主从redis,主机也就是用来写的机器,从机是从来读的,为主机分担压力,与集群不同的是redis哨兵不可通过从机写入数据同步到主机,但是也可以 ...
- python小兵 面向对象继承super和c3算法
python多继承 在前面的学习过程中. 我们已经知道了Python中类与类之间可以有继承关系. 当出现了x是一种y的的时候. 就可以使⽤继承关系. 即"is-a" 关系. 在继承 ...
- 面向计算机视觉的深度学习 | iBooker·ApacheCN
原文:Deep Learning for Computer Vision 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 不要担心自己的形象,只关心如何实现目标.--<原则>,生 ...
- php函数(parse_str()
parse_str()函数 把查询字符串解析到变量中 parse_str(string, array); string 规定要解析的字符串 array 存储变量的数组名称 例子: <?php p ...
- docker | jenkins 实现自动化部署项目,后端躺着把运维的钱挣了!(上)
前言 背景 最近在帮学校导师写项目,团队有4个人,项目前后端分离.如果是选择瀑布式开发:(在约定好接口的情况下)A.B同学写前端,C.D同学写后端,然后约定一个时间统一联调,最后将项目交付安装到客户机 ...
- SQL注入的原理及一般步骤
原理 SQL注入是一种攻击方式,在这种攻击方式中,恶意代码被插入到字符串中,然后该字符串传递到SQL Server的实例以进行分析和执行.任何构成SQL语句的过程都应进行注入检查,因为SQL Serv ...
- 【多线程与高并发原理篇:1_cpu多级缓存模型】
1. 背景 现代计算机技术中,cpu的计算速度远远高于主内存的读写速度.为了解决速度不匹配问题,充分利用cpu的性能,在cpu与主内存之间加入了多级缓存,也叫高速缓存,cpu读取数据直接从高速缓存中读 ...