1.1. 異常處理
1.1.1. 異常產生的原因及處理
異常是在編程時,一個意外的事件,如無效的輸入或連接丟失,當程序執行破壞的指令流時就會出現這種情況。Exception是Exceptional event的簡要表達。異常的實現需要保存拋出異常捕獲點的必要信息,這會一定程度上導致程序變慢,這也是人們詬病異常性能的原因。
異常(Exception)是一種非程序原因的操作失敗,而錯誤(Error)則意味着程序有缺陷。
Exception是一種類.例外會中斷執行堆棧直到被捕獲.一個異常可以用來傳達一個錯誤,但是更普遍的是用來表示出現了一些例外.
1.1.2. ASP.NET Core中啟動開發人員異常頁面
想要在應用程序中顯示詳細的異常信息,展示錯誤詳情的頁面在Microsoft.AspNetCore.Diagnostics
包中,但是自從ASP.NET Core 2.0開始Microsoft.AspNetCore.All
包里面包含了所有以Microsoft.AspNetCore
開頭的包,所以不需要在額外安裝Microsoft.AspNetCore.Diagnostics
包,但是需要在Startup
類的Configure
方法中進行配置
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
env.EnvironmentName = EnvironmentName.Production;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
將UseDeveloperExceptionPage
放在你想捕獲的中間件之前,如app.UseMvc
。
注:為了應用程序的安全,一般不會在生產環境中啟起用開發者頁面(異常頁面)。
示例
HomeController.cs 手動拋出一個異常
public class HomeController : Controller
{
public IActionResult Index()
{
throw new Exception("dddd");
return View();
}
}
運行結果
這樣的錯誤提示是很難讓開發人員定位出錯的位置;若要展示開發者頁面,還需要將應用程序環境設置為Development
。
開發者頁面有幾個標簽頁面包含了異常信息和請求參數的信息:
堆棧的詳細、查詢參數、Cookies信息和請求頭這些打印出來方便開發人員快速定位錯誤。但是程序在開發的時候可以直接這樣定位出錯位置,但是在生產環境中就需要借助日志將出錯信息寫到日志文件里面以方便開發人員定位錯誤。
1.2. 日志記錄
1.2.1. 日志作用
程序中記錄日志一般有兩個目的,故障定位和顯示程序運行狀態。好的日志記錄方式可以提供足夠多定位問題的依據。
1.2.2. 日志等級
如果有良好的習慣的人平時工作的時候會將領導交待下來的工作分為:緊急重要、重要不緊急、緊急不重要、不緊急不重要等;同樣ASP.NET Core也將日志定義了多個等級,從0到5總共6個等級:
- Trace = 0
這個級別只對開發人員調試有價值。這些消息可能包含敏感的應用程序數據,因此不應該在生產環境中啟用。
- Debug = 1
對於在開發和調試過程中具有短期可用性的信息。如果不是出現問題在生產環境一般不建議啟用。
- Information = 2
用於跟蹤應用程序,這些日志有長期的價值。
- Warning = 3
用於程序中的異常或意外事件。這些可能包括錯誤或其他不導致應用程序停止的條件,但是可能需要進行排查。
- Error = 4
對於不能處理的錯誤過異常。這些消息表明當前的活動或操作(例如當前的HTTP請求)失敗,而不是應用程序范圍的失敗。
- Critical = 5
對於那些需要立即關注的失敗。示例:磁盤空間中的數據丟失場景。
開發人員可以根據日志級別將眾多日志存儲到不到的介質中,以供分析用戶需求、定准程序錯誤等。
1.2.3. ASP.NET Core中的日志接口
ASP.NET Core支持可與各種日志記錄提供程序配合使用的日志記錄API。 內置提供程序允許您將日志發送到一個或多個目標,並且可以插入第三方日志記錄框架。
1.2.4. 實踐
用.NET Core內置日志接口將日志信息打印到控制台上
public class Startup
{
...
public virtual void Configure(IApplicationBuilder app)
{
...
app.UseExceptionHandler("/Home/Error");
...
}
}
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.Extensions.Logging;
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController (ILogger<HomeController> logger) {
_logger = logger;
}
public IActionResult Index () {
throw new Exception ("dddd");
return View ();
}
public IActionResult Error()
{
var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;
_logger.LogError("Oops!Error Info-----:", error);
return View("~/Views/Shared/Error.cshtml", error);
}
}
Program.cs
public class Program {
public static void Main (string[] args) {
BuildWebHost (args).Run ();
}
public static IWebHost BuildWebHost (string[] args) =>
WebHost.CreateDefaultBuilder (args)
.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);
config.AddEnvironmentVariables ();
})
.ConfigureLogging ((hostingContext, logging) => {
logging.AddConfiguration (hostingContext.Configuration.GetSection ("Logging"));
logging.AddConsole ();
logging.AddDebug ();
})
.UseStartup<Startup> ()
.Build ();
}
通過ASP.NET Core內置的日志支持可以很輕松將日志打印到控制台上面
注:
IExceptionHandlerFeature
接口位於Microsoft.AspNetCore.Diagnostics
包下,所以需要引入Microsoft.AspNetCore.Diagnostics
;ILogger
接口定義在Microsoft.Extensions.Logging.Abstractions
包,並且默認實現在Microsoft.Extensions.Logging
包里面。
但是,在生產環境中日志是記錄在數據庫或文件當中,下面以log4net作為例子將日志記錄到文件當中。
log4net 保存日志
在選擇log4net的時候最好選擇最新版,log4net從2.0.6開始支持.NET Core。
- 安裝log4net
執行dotnet restore
命令將nuget包拉到本地。確定本地.nuget文件夾里面有log4net並且版本號也一致。
- 配置log4net.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This section contains the log4net configuration settings -->
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
</appender>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<param name="Encoding" value="utf-8" />
<file value="log-file.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="Encoding" value="utf-8" />
<file value="logfile/" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<staticLogFileName value="false" />
<datePattern value="yyyyMMdd'.log'" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="FileAppender" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
</configuration>
這里定義了三個appender,都會起作用;日志等級為ALL將會記錄所有類型的日志。
Startup.cs
using System.IO;
using log4net;
using log4net.Config;
using log4net.Repository;
public class Startup
{
public static ILoggerRepository Repository;
public Startup (IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Repository = LogManager.CreateRepository ("NETCoreRepository");
XmlConfigurator.Configure (Repository, new FileInfo ("log4net.config"));
}
HomeController.cs
public class HomeController : Controller {
private ILog log = LogManager.GetLogger (Startup.Repository.Name, typeof (HomeController));
public IActionResult Index () {
throw new Exception ("Manul Exception:");
return View ();
}
public IActionResult Error () {
var feature = HttpContext.Features.Get<IExceptionHandlerFeature> ();
var error = feature?.Error;
log.Error(error);
return View ("~/Views/Shared/Error.cshtml", error);
}
}
運行結果
訪問首頁時會出異常,根據跳轉到錯誤路由Error
方法中;同時在項目中會生成兩個文件:
-
FileAppender生成的log-file.log日志文件
-
RollingLogFileAppender 是按時間生成的日志文件
控件台打印出來的日志文件
RollingLogFileAppender 打印的日志文件
FileAppender打段日志文件
從上面來看三種Appender打印的三種日志是一致的,所以在實際開發的時候可以根據需要來用哪種方式進行記錄日志。
1.3. 總結
本節講解了兩個知識點:
- 異常產生及異常和錯誤的區別,在ASP.NET Core中捕獲異常,啟動開發者異常頁面。
- ASP.NET Core內置日志接口和使用log4net將異常日志輸出到文件中
參與資料:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x
http://logging.apache.org/log4net/release/manual/configuration.html
作者:xdpie 出處: http://www.cnblogs.com/vipyoumay/p/7838069.html