這是Serilog系列的第三篇文章。html
做者:依樂祝mvc
在我上篇文章中,我描述瞭如何配置Serilog的RequestLogging中間件以向Serilog的請求日誌摘要中添加其餘屬性(例如請求主機名或選定的端點名稱)。這些屬性都在HttpContext
中可用,所以能夠由中間件自己直接添加。ui
其餘屬性,例如MVC特定的功能,像操做方法ID,RazorPages處理程序名稱或ModelValidationState,僅在MVC上下文中可用,所以Serilog的中間件不能直接訪問。.net
在本文中,我將展現如何建立action/page
過濾器來爲您記錄這些屬性,以便中間件能夠在後續建立日誌時訪問。日誌
Serilog的建立者Nicholas Blumhardt以前已經解決了這個話題。解決方案很是類似,儘管他在他的示例中建立了一個特性,您可使用該特性來裝飾actions/controllers。我在本文中跳過了這種方法,並要求將其全局應用,我但願這將是常見的解決方案。code
就目前而言,ASP.NET Core中的一個特徵是許多行爲被MVC「基礎結構」鎖定在了MVC框架內部來實現。端點路由是採用MVC功能並將其下移到核心框架中的首要工做之一。ASP.NET Core團隊一直在努力將更多MVC特定功能(例如模型綁定或操做結果)從MVC中移除,而後「下推」到核心框架中。有關此內容的更多信息,請參見Ryan Nowak在NDC上對Houdini項目的討論。htm
可是,就目前狀況而言,MVC內仍然存在一些不容易從應用程序其餘部分訪問的特性。當咱們考慮到咱們的Serilog的請求記錄中間件的時候,這意味着有些屬性咱們也是不容易記錄的。例如:中間件
OnGet
)1fbc88fa-42db-424f-b32b-c2d0994463f1
)MyController.SomeApiMethod (MyTestApp)
){action = "SomeApiMethod", controller = "My", page = ""}
)True
/ False
)在上一篇文章中我展現瞭如何使用RequestLogging中間件的擴展方法經過使用IDiagnosticContext
將附加屬性寫入Serilog的請求日誌中。這也僅適用於在HttpContext
可用的值。在這篇文章中,我將展現如何在過濾器中使用IDiagnosticContext
,以及將MVC特定值添加到日誌中。我還將展現如何在page過濾器中添加RazorPages特定的值(如HandlerName
)。
過濾器至關於爲每一個請求運行的相似於MVC的微型中間件管道。.NET Core MVC中有多種類型的過濾器,每種類型的過濾器在MVC過濾器管道中的有着不一樣的用途(有關更多詳細信息,請參見此文章)。在本文中,咱們將使用最多見的過濾器之一,即Action過濾器。
Action過濾器在執行MVC操做方法以前和以後運行。他們能夠訪問許多MVC屬性的值,例如正在執行的Action及其將被調用的參數。
下面的Action過濾器直接實現IActionFilter
。該OnActionExecuting
方法在調用action方法以前被調用,並將額外的MVC特定屬性添加到經過構造函數傳入的IDiagnosticContext
中。
public class SerilogLoggingActionFilter : IActionFilter { private readonly IDiagnosticContext _diagnosticContext; public SerilogLoggingActionFilter(IDiagnosticContext diagnosticContext) { _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); } public void OnActionExecuting(ActionExecutingContext context) { _diagnosticContext.Set("RouteData", context.ActionDescriptor.RouteValues); _diagnosticContext.Set("ActionName", context.ActionDescriptor.DisplayName); _diagnosticContext.Set("ActionId", context.ActionDescriptor.Id); _diagnosticContext.Set("ValidationState", context.ModelState.IsValid); } // Required by the interface public void OnActionExecuted(ActionExecutedContext context) { } }
在將MVC服務添加到應用程序中時,能夠在如下位置全局註冊過濾器Startup.ConfigureServices()
:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(opts => { opts.Filters.Add<SerilogLoggingPageFilter>(); }); // ... other service registration }
不管你使用
AddControllers
,AddControllersWithViews
,AddMvc
,或AddMvcCore
的方式你均可以採用一樣的方式來添加全局過濾器。
有了這個配置以後,若是你調用一個MVC控制器,你在Serilog的請求日誌消息中會看到額外的數據(ActionName
,ActionId
,和RouteData
,ValidationState
)記錄:
您能夠在此處將所需的任何其餘數據添加到日誌中。只需注意記錄參數值-切記不要記錄敏感或我的身份信息!
Nicholas Blumhardt在他的帖子中建議的Action過濾器是從
ActionFilterAttribute
派生的,所以能夠將其直接用做控制器和Action的特性。不幸的是,這意味着您必須使用服務定位來從每一個請求的HttpContext
中檢索單例的IDiagnosticContext
。個人方法能夠改用構造函數注入,可是不建議將其用做屬性,所以必須如上所述全局使用。並且,MVC將在個人實現中使用做用域生存期,而不是單例,所以它會在每一個請求中建立一個新實例。
若是要記錄其餘集中MVC過濾器中的值,則能夠以相同的方式實現其餘過濾器,例如資源過濾器,結果過濾器或受權過濾器。
上面實現的IActionFilter
過濾器在MVC和API控制器上可以正常運行,但它不會對RazorPages起做用。若是要爲選擇的給定Razor頁面記錄HandlerName,則須要建立一個自定義的IPageFilter
。
頁面過濾器直接相似於Action過濾器,但它們僅適用於Razor頁面。如下示例從PageHandlerSelectedContext
中檢索處理程序名稱並將其記錄爲屬性RazorPageHandler
。在這種狀況下,還須要一些樣板代碼,但過濾器的功能仍是很是基礎的-調用IDiagnosticContext.Set()
以記錄屬性。
public class SerilogLoggingPageFilter : IPageFilter { private readonly IDiagnosticContext _diagnosticContext; public SerilogLoggingPageFilter(IDiagnosticContext diagnosticContext) { _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); } //Required by the interface public void OnPageHandlerExecuted(PageHandlerExecutedContext context) { } public void OnPageHandlerExecuting(PageHandlerExecutingContext context) { } public void OnPageHandlerSelected(PageHandlerSelectedContext context) { var name = context.HandlerMethod?.Name ?? context.HandlerMethod?.MethodInfo.Name; if (name != null) { _diagnosticContext.Set("RazorPageHandler", name); } } }
請注意,咱們以前編寫的IActionFilter
代碼不會在Razor Pages上運行,所以,若是您也想記錄RazorPages RouteData
或ValidationState
RazorPages的其餘詳細信息,則也須要在此處添加它。該context
屬性包含您可能須要的大多數屬性,例如ModelState
和ActionDescriptor
。
接下來,您須要在Startup.ConfigureServices()
方法中註冊頁面過濾器:
public void ConfigureServices(IServiceCollection services) { //services.AddMvcCore( // opts => opts.Filters.Add<SerilogLoggingPageFilter>() // ); services.AddRazorPages().AddMvcOptions( opts => opts.Filters.Add<SerilogLoggingPageFilter>() ) ; }
添加過濾器後,對「Razor頁面」的請求如今能夠看到添加的附加屬性,IDiagnosticContext
這些屬性將添加到Serilog請求日誌中。請參見下圖中的RazorPageHandler
屬性:
默認狀況下,當用Serilog的請求日誌記錄中間件替換ASP.NET Core基礎結構中的日誌記錄時,您會丟失一些信息(與開發環境的默認配置相比)。在本文中,我將展現如何自定義Serilog,RequestLoggingOptions
以從新添加特定於MVC的其餘屬性。
要將與MVC相關的屬性添加到Serilog請求日誌中,請建立一個IActionFilter
並使用IDiagnosticContext.Set()
來添加屬性。要將與Razor頁面相關的屬性添加到Serilog請求日誌中,請在IPageFilter
中使用IDiagnosticContext
的相同方法建立和添加屬性。
下一節讓咱們一塊兒探討下如何從Serilog請求記錄中排除運行情況檢查端點。