玩轉ASP.NET Core中的日誌元件
簡介
日誌元件,作為程式設計師使用頻率最高的元件,給程式設計師開發除錯程式提供了必要的資訊。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.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;
}
}
程式碼解釋
- 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僅針對一種日誌級別
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整合的日誌提供器,我們希望產生的日誌通過一個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, 效果如下
參考文獻
作者:Lamond Lu
相關文章
- ASP.NET Core視覺化日誌元件使用ASP.NET視覺化元件
- 理解ASP.NET Core - 日誌(Logging)ASP.NET
- asp.net core使用serilog將日誌推送到騰訊雲日誌服務ASP.NET
- 【ASP.NET Core】使用SignalR推送伺服器日誌ASP.NETSignalR伺服器
- (精華)2020年7月20日 ASP.NET Core serilog日誌框架的使用ASP.NET框架
- ASP.NET Core 整合測試中通過 Serilog 向控制檯輸出日誌ASP.NET
- ASP.NET Core 2.2 基礎知識(八)【日誌記錄】ASP.NET
- ASP.NET Core之OpenTelemetry、SEQ構建視覺化日誌ASP.NET視覺化
- .Net Core中的診斷日誌DiagnosticSource講解
- 開發日誌:Kylin麒麟作業系統部署ASP.NET CORE作業系統ASP.NET
- ASP.NET Core之Serilog、SEQ、ILogger構建視覺化日誌ASP.NET視覺化
- .net core中的那些常用的日誌框架(Logging篇)框架
- .net core中的那些常用的日誌框架(NLog篇)框架
- .net core中的那些常用的日誌框架(Serilog篇)框架
- GO的日誌怎麼玩Go
- (精華)2020年7月20日 ASP.NET Core log4.net日誌框架的使用ASP.NET框架
- 一套標準的ASP.NET Core容器化應用日誌收集分析方案ASP.NET應用日誌
- 學習ASP.NET Core(10)-全域性日誌與xUnit系統測試ASP.NET
- .NET Core 中的日誌與分散式鏈路追蹤分散式
- ASP.NET Core擴充套件庫之日誌ASP.NET套件
- 一起玩轉玩轉LiteOS元件:TinyFrame元件
- Blazor入門:ASP.NET Core Razor 元件BlazorASP.NET元件
- 【asp.net core 系列】14 .net core 中的IOCASP.NET
- ASP.NET Core 中的快取ASP.NET快取
- 聊聊ASP.NET Core中的配置ASP.NET
- 玩轉ASP.NET 6.0框架-序言ASP.NET框架
- .NET Core開發日誌——HttpClientFactoryHTTPclient
- .NET Core開發日誌——Startup
- .NET Core開發日誌——Middleware
- 一個完整的go 日誌元件Go元件
- Hyperf日誌檢視元件元件
- ASP.NET Core 中的管道機制ASP.NET
- ASP.NET Core 中的依賴注入ASP.NET依賴注入
- 程式中的日誌
- Linux下玩轉nginx系列(三)---nginx日誌配置檔案說明LinuxNginx
- .NET Core使用Nlog記錄日誌
- 智慧手環core日誌獲取
- .Net Core 審計日誌實現