日誌組件,做爲程序員使用頻率最高的組件,給程序員開發調試程序提供了必要的信息。ASP.NET Core中內置了一個通用日誌接口ILogger
,並實現了多種內置的日誌提供器,例如javascript
除了內置的日誌提供器,ASP.NET Core還支持了多種第三方日誌工具,例如html
開發人員在ASP.Net Core中能夠自由指定日誌提供器,並將日誌發送到指定的位置。
本篇博文中,咱們將由淺入深的介紹ASP.Net Core中通用日誌接口,最後咱們將實現一些自定義的日誌提供器(Log Provider)。java
ASP.NET Core中提供了6種日誌級別,分別是Trace, Debug, Information, Warning, Error, Critical。如下是他們的具體使用場景jquery
日誌級別 | 經常使用場景 |
---|---|
Trace | 記錄一些對程序員調試問題有幫助的信息, 其中可能包含一些敏感信息, 因此應該避免在 生產環境中啓用Trace日誌 |
Debug | 記錄一些在開發和調試階段有用的短時變 量(Short-term usefulness), 因此除非爲了臨時排除生產環境的 故障,開發人員應該儘可能避免在生產環境中啓用Debug日誌 |
Information | 記錄應用程序的一些流程, 例如,記錄當前api請求的url |
Warning | 記錄應用程序中發生的不正常或者未預期的事件信息。 這些信息中可能包含錯誤消息或者錯誤產生的條件, 例如, 文件未找到 |
Error | 記錄應用程序中某個操做產生的錯誤和異常信息。 |
Critical | 記錄一些須要馬上修復的問題。例如數據丟失,磁盤空間不足。 |
爲了建立一個日誌,咱們首先須要經過依賴注入獲取一個實現ILogger <日誌類別> 的泛型接口對象。 程序員
已如下代碼爲例, 在ValuesController
的構造函數中,咱們注入了一個ILogger
的日誌記錄器json
[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"。api
注:
ILogger
爲了提供了6個可用的輸出日誌方法,分別對應了6個不一樣的日誌級別跨域
- LogTrace
- LogDebug
- LogInformation
- LogWarning
- LogError
- LogCritical!
下面咱們使用Kestral服務器啓動項目
服務器
項目產生的日誌以下,咱們手動輸出的日誌出如今控制檯中。app
可能針對以上的代碼,有些同窗可能有疑問,咱們沒有在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.AddConsole
和logging.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
, ILoggerProvider
2個接口來建立咱們本身的日誌提供器。
這裏咱們但願添加一個在控制檯中輸出的日誌,可是和內置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; } }
代碼解釋
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() { } }
代碼解釋
CreateLogger
是ILoggerProvider
接口中定義的方法,它是用來返回一個日誌生成器的最後咱們修改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服務器推送到一個網頁中。
首先咱們建立一個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; } }
代碼解釋
而後咱們修改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而後咱們建立一個另一個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" }; } }
代碼解釋
最後咱們修改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, 效果以下