如何使用Serilog.AspNetCore記錄ASP.NET Core3.0的MVC屬性
這是Serilog系列的第三篇文章。
- 第1部分-使用Serilog RequestLogging減少日誌詳細程度
- 第2部分-使用Serilog記錄所選的終結點屬性
- 第3部分-使用Serilog.AspNetCore記錄MVC屬性(本文)
- 第4部分-從Serilog請求記錄中排除執行狀況檢查端點
作者:依樂祝
譯文地址:https://www.cnblogs.com/yilezhu/p/12243984.html
原文地址:https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-logging-mvc-propertis-with-serilog/
在我上篇文章中,我描述瞭如何配置Serilog的RequestLogging中介軟體以向Serilog的請求日誌摘要中新增其他屬性(例如請求主機名或選定的端點名稱)。這些屬性都在HttpContext
中可用,因此可以由中介軟體本身直接新增。
其他屬性,例如MVC特定的功能,像操作方法ID,RazorPages處理程式名稱或ModelValidationState,僅在MVC上下文中可用,因此Serilog的中介軟體不能直接訪問。
在本文中,我將展示如何建立action/page
過濾器來為您記錄這些屬性,以便中介軟體可以在後續建立日誌時訪問。
Serilog的建立者Nicholas Blumhardt之前已經解決了這個話題。解決方案非常相似,儘管他在他的示例中建立了一個特性,您可以使用該特性來裝飾actions/controllers
。我在本文中跳過了這種方法,並要求將其全域性應用,我希望這將是常見的解決方案。
記錄來自MVC的其他資訊
就目前而言,ASP.NET Core中的一個特徵是許多行為被MVC“基礎結構”鎖定在了MVC框架內部來實現。端點路由是採用MVC功能並將其下移到核心框架中的首要工作之一。ASP.NET Core團隊一直在努力將更多MVC特定功能(例如模型繫結或操作結果)從MVC中移除,然後“下推”到核心框架中。有關此內容的更多資訊,請參見Ryan Nowak在NDC上對Houdini專案的討論。
但是,就目前情況而言,MVC內仍然存在一些不容易從應用程式其他部分訪問的特性。當我們考慮到我們的Serilog的請求記錄中介軟體的時候,這意味著有些屬性我們也是不容易記錄的。例如:
- HandlerName(
OnGet
) - ActionId(
1fbc88fa-42db-424f-b32b-c2d0994463f1
) - ActionName (
MyController.SomeApiMethod (MyTestApp)
) - RouteData(
{action = "SomeApiMethod", controller = "My", page = ""}
) - ValidationState(
True
/False
)
在上一篇文章中我展示瞭如何使用RequestLogging中介軟體的擴充套件方法通過使用IDiagnosticContext
將附加屬性寫入Serilog的請求日誌中。這也僅適用於在HttpContext
可用的值。在這篇文章中,我將展示如何在過濾器中使用IDiagnosticContext
,以及將MVC特定值新增到日誌中。我還將展示如何在page過濾器中新增RazorPages特定的值(如HandlerName
)。
使用自定義過濾器記錄MVC屬性
過濾器相當於為每個請求執行的類似於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過濾器中的值,則可以以相同的方式實現其他過濾器,例如資源過濾器,結果過濾器或授權過濾器。
使用自定義page過濾器記錄RazorPages屬性
上面實現的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請求記錄中排除執行狀況檢查端點