简介

日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息。ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如

  • Console
  • Debug
  • EventSource
  • EventLog
  • TraceSource
  • Azure App Service

除了内置的日志提供器,ASP.NET Core还支持了多种第三方日志工具,例如

开发人员在ASP.Net Core中可以自由指定日志提供器,并将日志发送到指定的位置。

本篇博文中,我们将由浅入深的介绍ASP.Net Core中通用日志接口,最后我们将实现一些自定义的日志提供器(Log Provider)。

使用系统提供的内置日志提供器

日志级别(Log Level)

ASP.NET Core中提供了6种日志级别,分别是Trace, Debug, Information, Warning, Error, Critical。以下是他们的具体使用场景

日志级别 常用场景
Trace 记录一些对程序员调试问题有帮助的信息,
其中可能包含一些敏感信息, 所以应该避免在
生产环境中启用Trace日志
Debug 记录一些在开发和调试阶段有用的短时变
量(Short-term usefulness), 所以除非为了临时排除生产环境的
故障,开发人员应该尽量避免在生产环境中启用Debug日志
Information 记录应用程序的一些流程, 例如,记录当前api请求的url
Warning 记录应用程序中发生的不正常或者未预期的事件信息。
这些信息中可能包含错误消息或者错误产生的条件, 例如, 文件未找到
Error 记录应用程序中某个操作产生的错误和异常信息。
Critical 记录一些需要立刻修复的问题。例如数据丢失,磁盘空间不足。

如何创建日志

为了创建一个日志,我们首先需要通过依赖注入获取一个实现ILogger<日志类别>的泛型接口对象。

已以下代码为例, 在ValuesController的构造函数中,我们注入了一个ILogger的日志记录器

    [Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ILogger<ValuesController> _logger = null; public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
} // GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
_logger.LogInformation("[Test Log]Getting items.");
return new string[] { "value1", "value2" };
} }

然后我们使用ILogger接口提供的LogInformation方法添加了一个Information类型日志"[Test Log]Getting items"。

注:ILogger为了提供了6个可用的输出日志方法,分别对应了6个不同的日志级别

  • LogTrace
  • LogDebug
  • LogInformation
  • LogWarning
  • LogError
  • LogCritical!

下面我们使用Kestral服务器启动项目

项目产生的日志如下,我们手动输出的日志出现在控制台中。

日志配置

可能针对以上的代码,有些同学可能有疑问,我们没有在Startup.cs中注入任何日志提供器,但是日志却正常产生了。这是由于Program.cs中默认使用WebHost.CreateDefaultBuilder方法添加了2个日志提供器。

默认日志提供器

当创建一个新的ASP.NET Core WebApi项目,我们通常在Program.cs中看到以下代码。

    public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
} public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}

下面我们看一下WebHost.CreateDefaultBuilder的源代码

    public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.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())
{
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) =>
{
logging.UseConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseIISIntegration()
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
})
.ConfigureServices(services =>
{
services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
}); return builder;
}

你会发现代码中通过logging.AddConsolelogging.AddDebug默认配置了Console和Debug类型的日志提供器,这也就是为什么我们没有注入任何日志提供器,日志却能正常产生了。

手动添加日志提供器

看了以上代码后,你应该可以也清楚了如何自己添加其他内置的日志提供器。我们只需要在Program.cs中使用ConfigureLogging方法就可以配置我们需要的日志提供器了。

例:

    public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
} public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
})
.UseStartup<Startup>();
}

除了在Program.cs添加日志提供器之外,我们还可以在Startup.cs中添加日志提供器。

在Startup.cs中,我们可以为Configure方法添加第三个参数ILoggerFactory loggerFactory, 并使用该参数添加日志提供器。

例:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} loggerFactory.AddConsole();
loggerFactory.AddDebug(); app.UseMvc();
}

配置文件及日志级别过滤

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": "Trace"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}

以上代码中的Debug表示Debug日志提供器, Console表示Console日志提供器, 最后一个LogLevel表示其他日志提供器通用。

Debug中的Default设置为Information, 即Debug中产生的日志最低级别是Information, 低于Information级别的日志不会输出。Console中的配置同理。

自定义日志组件

在学习了以上基础知识之后,我们应该对内置的日志提供器有了简单的认识。下面我们尝试自定义2个日志提供器。

在ASP.NET Core中,我们可以通过实现ILogger, ILoggerProvider2个接口来创建我们自己的日志提供器。

编写一个自定义样式的控制台日志组件

这里我们希望添加一个在控制台中输出的日志,但是和内置Console类型日志的区别是我们为不同的日志类型设置了不同的颜色。

首先我们创建一个新的Api项目ColoredConsoleLoggerSample

然后我们创建一个针对不同日志级别的字体颜色配置类ColoredConsoleLoggerConfiguration, 代码如下

    public class ColoredConsoleLoggerConfiguration
{
public LogLevel LogLevel { get; set; } = LogLevel.Warning;
public ConsoleColor Color { get; set; } = ConsoleColor.Yellow;
}

这个类中定义了针对不同日志类型设置不同的字体颜色。

然后我们创建一个日志类ColoredConsoleLogger, 它实现了ILogger接口,代码如下

    public class ColoredConsoleLogger : ILogger
{
private readonly string _name;
private readonly ColoredConsoleLoggerConfiguration _config; public ColoredConsoleLogger(string name, ColoredConsoleLoggerConfiguration config)
{
_name = name;
_config = config;
} public IDisposable BeginScope<TState>(TState state)
{
return null;
} public bool IsEnabled(LogLevel logLevel)
{
return logLevel == _config.LogLevel;
} public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
} var color = Console.ForegroundColor;
Console.ForegroundColor = _config.Color;
Console.WriteLine($"{logLevel.ToString()} - {_name} - {formatter(state, exception)}");
Console.ForegroundColor = color;
}
}

代码解释

  • ColoredConsoleLogger仅针对一种日志级别
  • 只要当前产生的日志级别和ColoredConsoleLogger中定义的日志级别一样时,日志才会输出,这里我们是用IsEnable方法判断的
  • Log是ILogger接口中定义的方法,我们就是在这个方法中输出日志的
  • 这里我们在输入日志前记录下了当前控制台的原始字体颜色, 当输出日志完成之后,我们将字体颜色恢复成了原来的颜色

然后我们添加一个Logger提供器类ColoredConsoleLoggerProvider,代码如下

    public class ColoredConsoleLoggerProvider : ILoggerProvider
{
private readonly ColoredConsoleLoggerConfiguration _config; public ColoredConsoleLoggerProvider(ColoredConsoleLoggerConfiguration config)
{
_config = config;
} public ILogger CreateLogger(string categoryName)
{
return new ColoredConsoleLogger(categoryName, _config);
} public void Dispose()
{ }
}

代码解释

  • ColoredConsoleLoggerProvider仅针对一种日志级别
  • CreateLoggerILoggerProvider接口中定义的方法,它是用来返回一个日志生成器的

最后我们修改Startup.cs中的Configure方法, 代码如下

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} loggerFactory.AddProvider(new ColoredConsoleLoggerProvider(new ColoredConsoleLoggerConfiguration
{
LogLevel = LogLevel.Information,
Color = ConsoleColor.Blue
}));
loggerFactory.AddProvider(new ColoredConsoleLoggerProvider(new ColoredConsoleLoggerConfiguration
{
LogLevel = LogLevel.Debug,
Color = ConsoleColor.Gray
})); app.UseMvc();
}

这里我们添加了2个日志日志提供器,分别是针对Information级别日志和Debug级别日志的

最终效果

我们的日志根据我们预设的字体颜色正确的显示了出来

编写一个与SignalR集成的实时日志组件

下面我们再来自定义一个与SignalR集成的日志提供器,我们希望产生的日志通过一个SignalR服务器推送到一个网页中。

首先我们创建一个ASP.NET Core WebApi项目,命名为SignalrServer, 创建之后,我们右键项目属性,修改App Url为http://localhost:5000

然后我们创建一个LogHub类,它集成自Hub类,代码如下

    public class LogHub : Hub
{
public async Task WriteLog(Log log)
{
await Clients.All.SendAsync("showLog", log);
}
} public class Log
{
public LogLevel Level { get; set; } public string Content { get; set; }
}

代码解释

  • 这里我们创建了一个写日志的方法,它会把日志推送到所有连接到SignalR服务器的客户端,并调用客户端的showLog方法来展示推送的日志信息。

然后我们修改Startup.cs文件,代码如下

    public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSignalR();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
app.UseSignalR(routes =>
{
routes.MapHub<LogHub>("/logHub");
}); app.UseMvc();
}
}

代码解释

  • 我们通过service.AddSignalR注册了SignalR服务
  • 我们通过app.UserSignalR方法注册一个logHub
  • 这里我们启用了CORS, 因为需要提供跨域访问

然后我们创建一个另外一个ASP.NET Core WebApi项目, SignalRLoggerSample

项目创建成功之后,我们右键点击项目属性,并设置App URL为http://localhost:5001

然后我们使用Package Console Manager, 安装Microsoft.AspNetCore.SignalR.Client

PM> install-package Microsoft.AspNetCore.SignalR.Client

为了创建一个SignalR日志提供器, 我们分别创建一个SignalRLogger类和一个SignalRLoggerProvider类, 代码如下

SignalRLogger.cs

    public class SignalRLogger : ILogger
{
HubConnection connection; public SignalRLogger()
{
connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/LogHub")
.Build();
} public IDisposable BeginScope<TState>(TState state)
{
return null;
} public bool IsEnabled(LogLevel logLevel)
{
return true;
} public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
} connection.StartAsync().Wait();
var task = connection.SendAsync("writeLog", new { Level = logLevel, Content = formatter(state, exception) });
task.Wait();
}
}

SignalRLoggerProvider.cs

    public class SignalRLoggerProvider : ILoggerProvider
{
public SignalRLoggerProvider()
{
} public ILogger CreateLogger(string categoryName)
{
return new SignalRLogger();
} public void Dispose()
{ }
}

代码解释

  • 这里使用HubConnectionBuilder创建了一个SignalR连接
  • 连接启动成功之后,我们使用connection.SendAsync方法,将当前产生的日志信息发送到SignalR服务器

添加完成之后,我们在wwwroot文件夹中创建一个index.html, 在其中引入jquery和signalr的js库,并指定连接的signalR服务器是http://localhost:5000/logHub

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="jquery-1.10.2.min.js"></script>
<script src="signalr.min.js"></script>
</head>
<body>
<h1>Logs</h1>
<div id="content" style="border:1px solid #0094ff"> </div>
<script type="text/javascript">
var levels = [
{ level: 0, name: 'Trace', backgroundColor: 'gray' },
{ level: 1, name: 'Debug', backgroundColor: 'green' },
{ level: 2, name: 'Information', backgroundColor: 'blue' },
{ level: 3, name: 'Warning', backgroundColor: 'yellow' },
{ level: 4, name: 'Error', backgroundColor: 'orange' },
{ level: 5, name: 'Critical', backgroundColor: 'red' },
]; function getLevelName(level) {
return levels.find(function (o) {
return o.level == level;
}).name;
} function getLevelColor(level) {
return levels.find(function (o) {
return o.level == level;
}).backgroundColor;
} var connection = new signalR.HubConnectionBuilder().withUrl("http://localhost:5000/logHub").build(); connection.on("showLog", function (message) {
var div = "<div style='background-color:" + getLevelColor(message.level)+"'>[" + getLevelName(message.level) + "]:" + message.content + "</div>";
$("#content").append(div);
}); connection.start().catch(function (err) {
return console.error(err.toString());
});
</script>
</body>
</html>

然后我们修改ValuesController文件,代码如下

    [Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private ILogger<ValuesController> _logger = null; public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
} // GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
_logger.LogTrace("User call the /api/values api");
_logger.LogDebug("User call the /api/values api");
_logger.LogInformation("User call the /api/values api");
_logger.LogWarning("User call the /api/values api");
_logger.LogError("User call the /api/values api");
_logger.LogCritical("User call the /api/values api");
return new string[] { "value1", "value2" };
}
}

代码解释

  • 我们创建了一个ValueController类的日志
  • 当用户请求/api/values时,我们输出了6个不同级别的日志

最后我们修改Startup.cs中的Configure方法,使用我们之前介绍的方法,将SignalRLoggerProvider添加到管道中

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles(); loggerFactory.AddProvider(new SignalRLoggerProvider()); app.UseMvc(); }

最终效果

最后我们按照顺序,先启动SignalRServer, 再启动SignalRLoggerSample, 效果如下

本篇源代码

参考文献

玩转ASP.NET Core中的日志组件的更多相关文章

  1. (14)ASP.NET Core 中的日志记录

    1.前言 ASP.NET Core支持适用于各种内置和第三方日志记录提供应用程序的日志记录API.本文介绍了如何将日志记录API与内置提供应用程序一起使用. 2.添加日志提供程序 日志记录提供应用程序 ...

  2. Asp.Net Core中利用Seq组件展示结构化日志功能

    在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...

  3. ASP.NET Core 中的日志记录

    目录 内置日志的使用 使用Nlog 集成ELK 参考 内置日志的使用 Logger 是 asp .net core 的内置 service,所以我们就不需要在ConfigureService里面注册了 ...

  4. .Net Core中的日志组件(Logging)

    1.介绍 Logging组件是微软实现的日志记录组件包括控制台(Console).调试(Debug).事件日志(EventLog)和TraceSource,但是没有实现最常用用的文件记录日志功能(可以 ...

  5. 【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

    前言 Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0 ...

  6. ASP.NET Core 异常处理与日志记录

    1. ASP.NET Core 异常处理与日志记录 1.1. 异常处理 1.1.1. 异常产生的原因及处理 1.1.2. ASP.NET Core中启动开发人员异常页面 1.2. 日志记录 1.2.1 ...

  7. asp.net core 集成 log4net 日志框架

    asp.net core 集成 log4net 日志框架 Intro 在 asp.net core 中有些日志我们可能想输出到数据库或文件或elasticsearch等,如果不自己去实现一个 Logg ...

  8. .NET Core 中的日志与分布式链路追踪

    目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...

  9. ASP.NET Core中的ActionFilter与DI

    一.简介 前几篇文章都是讲ASP.NET Core MVC中的依赖注入(DI)与扩展点的,也许大家都发现在ASP.NET CORE中所有的组件都是通过依赖注入来扩展的,而且面向一组功能就会有一组接口或 ...

随机推荐

  1. Python3-Cookbook总结 - 第三章:数字日期和时间

    第三章:数字日期和时间 在Python中执行整数和浮点数的数学运算时很简单的. 尽管如此,如果你需要执行分数.数组或者是日期和时间的运算的话,就得做更多的工作了. 本章集中讨论的就是这些主题. Con ...

  2. python 判断连个 Path 是否是相同的文件夹

    python 判断连个 Path 是否是相同的文件夹 import os os.path.normcase(p1) == os.path.normcase(p2) normcase() 在 windo ...

  3. Emgucv使用中常用函数总结

    Emgucv常用函数总结: 读取图片 Mat SCr = new Mat(Form1.Path, Emgu.CV.CvEnum.LoadImageType.AnyColor); //根据路径创建指定的 ...

  4. 关于eclipse使用thymeleaf时,提示标签不显示及后续问题的解方法

    因为thymeleaf 使用快捷键提示,不提示标签信息. 在使用网上说的的install new software安装插件的时候 报错: Unable to read repository at ht ...

  5. 微软官网tools

    DHCP/AD域插件: 远程管理工具(含DHCP/AD域) 安装网址: https://www.microsoft.com/zh-cn/download/details.aspx?id=7887 程序 ...

  6. LNMP环境并发优化

    LNMP环境并发优化 服务器 8核32Gx3 如图是一条http请求的生命周期,共经过nginx,php-fpm,PHP三个模块 所以我们可以从nginx,php-fpm,PHP三个维度去优化 一.p ...

  7. <算法图解>读书笔记:第3章 递归

    第3章 递归 3.1 递归 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一 ...

  8. Windows远程桌面相关

    3389端口是Windows系统远程终端服务以及系统远程桌面服务默认所使用的端口,原本是为了管理人员能够方便的远程维护管理计算机而设计的,但如今已经成为了黑客们最喜爱的一种人侵途径,入侵者通过3389 ...

  9. 编译ROCKSDB总结

    Rocksdb是挺好的一个东西,就是取得一个可用的库太麻烦.之前我是用的rocksdbsharp里面他有编译好windows 和 linux的库 兼 容性还挺好,ubuntu win10 直接跑没毛病 ...

  10. 把ssl模块加入到已经编译好的apache中实现HTTPS

    为了使Apache支持https访问,系统需要安有apache.openssl.mod_ssl.so 1.安装openssl: 基本上系统都已经安装了,在/usr/bin/openssl下,直接使用o ...