ASP.NET Core管道詳解[3]: Pipeline = IServer + IHttpApplication

一、服務(wù)器(IServer)

由于服務(wù)器是整個(gè)請(qǐng)求處理管道的“龍頭”外永,所以啟動(dòng)和關(guān)閉應(yīng)用的最終目的是啟動(dòng)和關(guān)閉服務(wù)器。ASP.NET Core框架中的服務(wù)器通過IServer接口來表示拧咳,該接口具有如下所示的3個(gè)成員伯顶,其中由服務(wù)器提供的特性就保存在其Features屬性表示的IFeatureCollection集合中。IServer接口的StartAsync<TContext>方法與StopAsync方法分別用來啟動(dòng)和關(guān)閉服務(wù)器。

publicinterface IServer : IDisposable

{

? ? IFeatureCollection Features { get; }

? ? Task StartAsync(IHttpApplication application, CancellationToken cancellationToken);

? ? Task StopAsync(CancellationToken cancellationToken);

}

服務(wù)器在開始監(jiān)聽請(qǐng)求之前總是綁定一個(gè)或者多個(gè)監(jiān)聽地址祭衩,這個(gè)地址是應(yīng)用程序從外部指定的灶体。具體來說,應(yīng)用程序指定的監(jiān)聽地址會(huì)封裝成一個(gè)特性掐暮,并且在服務(wù)器啟動(dòng)之前被添加到它的特性集合中蝎抽。這個(gè)承載了監(jiān)聽地址列表的特性通過如下所示的IServerAddressesFeature接口來表示,該接口除了有一個(gè)表示地址列表的Addresses屬性路克,還有一個(gè)布爾類型的PreferHostingUrls屬性樟结,該屬性表示如果監(jiān)聽地址同時(shí)設(shè)置到承載系統(tǒng)配置和服務(wù)器上,是否優(yōu)先考慮使用前者精算。

publicinterface IServerAddressesFeature

{

? ? ICollection Addresses {get; }

? ? boolPreferHostingUrls {get;set; }

}

正如前面所說瓢宦,服務(wù)器將用來處理由它接收請(qǐng)求的處理器會(huì)被視為一個(gè)通過IHttpApplication<TContext>接口表示的應(yīng)用,所以可以將ASP.NET Core的請(qǐng)求處理管道視為IServer對(duì)象和IHttpApplication<TContext>對(duì)象的組合灰羽。當(dāng)調(diào)用IServer對(duì)象的StartAsync<TContext>方法啟動(dòng)服務(wù)器時(shí)驮履,我們需要提供這個(gè)用來處理請(qǐng)求的IHttpApplication<TContext>對(duì)象。IHttpApplication<TContext>采用基于上下文的請(qǐng)求處理方式廉嚼,泛型參數(shù)TContext代表的就是上下文的類型玫镐。在IHttpApplication<TContext>處理請(qǐng)求之前,它需要先創(chuàng)建一個(gè)上下文對(duì)象前鹅,該上下文會(huì)在請(qǐng)求處理結(jié)束之后被釋放摘悴。上下文的創(chuàng)建、釋放和自身對(duì)請(qǐng)求的處理實(shí)現(xiàn)在該接口3個(gè)對(duì)應(yīng)的方法(CreateContext舰绘、DisposeContext和ProcessRequestAsync)中蹂喻。

publicinterfaceIHttpApplication{

? ? TContext CreateContext(IFeatureCollection contextFeatures);

? ? void DisposeContext(TContext context, Exception exception);

? ? Task ProcessRequestAsync(TContext context);

}

二、承載應(yīng)用( HostingApplication)

ASP.NET Core框架利用如下所示的HostingApplication類型作為IHttpApplication<TContext>接口的默認(rèn)實(shí)現(xiàn)捂寿,它使用一個(gè)內(nèi)嵌的Context類型來表示處理請(qǐng)求的上下文口四。一個(gè)Context對(duì)象是對(duì)一個(gè)HttpContext對(duì)象的封裝,同時(shí)承載了一些與診斷相關(guān)的信息秦陋。

publicclassHostingApplication : IHttpApplication{

? ? ...

? ? publicstruct Context

? ? {

? ? ? ? publicHttpContext HttpContext {get;set; }

? ? ? ? publicIDisposable Scope {get;set; }

? ? ? ? publiclongStartTimestamp {get;set; }

? ? ? ? publicboolEventLogEnabled {get;set; }

? ? ? ? publicActivity Activity {get;set; }

? ? }

}

HostingApplication對(duì)象會(huì)在開始和完成請(qǐng)求處理蔓彩,以及在請(qǐng)求過程中出現(xiàn)異常時(shí)發(fā)出一些診斷日志事件。具體來說驳概,HostingApplication對(duì)象會(huì)采用3種不同的診斷日志形式赤嚼,包括基于DiagnosticSource和EventSource的診斷日志以及基于 .NET Core日志系統(tǒng)的日志。Context除HttpContext外的其他屬性都與診斷日志有關(guān)顺又。具體來說更卒,Context的Scope是為ILogger創(chuàng)建的針對(duì)當(dāng)前請(qǐng)求的日志范圍(第9章有對(duì)日志范圍的詳細(xì)介紹),此日志范圍會(huì)攜帶唯一標(biāo)識(shí)每個(gè)請(qǐng)求的ID稚照,如果注冊(cè)ILoggerProvider提供的ILogger支持日志范圍蹂空,它可以將這個(gè)請(qǐng)求ID記錄下來俯萌,那么我們就可以利用這個(gè)ID將針對(duì)同一請(qǐng)求的多條日志消息組織起來做針對(duì)性分析。

HostingApplication對(duì)象會(huì)在請(qǐng)求結(jié)束之后記錄當(dāng)前請(qǐng)求處理的耗時(shí)上枕,所以它在開始處理請(qǐng)求時(shí)就會(huì)記錄當(dāng)前的時(shí)間戳咐熙,Context的StartTimestamp屬性表示開始處理請(qǐng)求的時(shí)間戳。它的EventLogEnabled屬性表示針對(duì)EventSource的事件日志是否開啟辨萍,而Activity屬性則與針對(duì)DiagnosticSource的診斷日志有關(guān)棋恼,Activity代表基于當(dāng)前請(qǐng)求處理的活動(dòng)。

雖然ASP.NET Core應(yīng)用的請(qǐng)求處理完全由HostingApplication對(duì)象負(fù)責(zé)分瘦,但是該類型的實(shí)現(xiàn)邏輯其實(shí)是很簡單的蘸泻,因?yàn)樗鼘⒕唧w的請(qǐng)求處理分發(fā)給一個(gè)RequestDelegate對(duì)象,該對(duì)象表示的正是所有注冊(cè)中間件組成的委托鏈嘲玫。在創(chuàng)建HostingApplication對(duì)象時(shí)除了需要提供RequestDelegate對(duì)象悦施,還需要提供用于創(chuàng)建HttpContext上下文的IHttpContextFactory對(duì)象,以及與診斷日志有關(guān)的ILogger對(duì)象和DiagnosticListener對(duì)象去团,它們被用來創(chuàng)建上面提到過的HostingApplicationDiagnostics對(duì)象抡诞。

publicclassHostingApplication : IHttpApplication{

? ? privatereadonly RequestDelegate _application;

? ? private HostingApplicationDiagnostics _diagnostics;

? ? privatereadonly IHttpContextFactory _httpContextFactory;

? ? public HostingApplication(RequestDelegate application, ILogger logger,DiagnosticListener diagnosticSource, IHttpContextFactory httpContextFactory)

? ? {

? ? ? ? _application = application;

? ? ? ? _diagnostics =new HostingApplicationDiagnostics(logger, diagnosticSource);

? ? ? ? _httpContextFactory = httpContextFactory;

? ? }

? ? public Context CreateContext(IFeatureCollection contextFeatures)

? ? {

? ? ? ? varcontext =new Context();

? ? ? ? varhttpContext = _httpContextFactory.Create(contextFeatures);

? ? ? ? _diagnostics.BeginRequest(httpContext, ref context);

? ? ? ? context.HttpContext = httpContext;

? ? ? ? return context;

? ? }

? ? publicTask ProcessRequestAsync(Context context) => _application(context.HttpContext);

? ? publicvoid DisposeContext(Context context, Exception exception)

? ? {

? ? ? ? varhttpContext = context.HttpContext;

? ? ? ? _diagnostics.RequestEnd(httpContext, exception, context);

? ? ? ? _httpContextFactory.Dispose(httpContext);

? ? ? ? _diagnostics.ContextDisposed(context);

? ? }

}

如上面的代碼片段所示,當(dāng)CreateContext方法被調(diào)用時(shí)土陪,HostingApplication對(duì)象會(huì)利用IHttpContextFactory工廠創(chuàng)建出當(dāng)前HttpContext上下文昼汗,并進(jìn)一步將它封裝成一個(gè)Context對(duì)象。在返回這個(gè)Context對(duì)象之前鬼雀,它會(huì)調(diào)用HostingApplicationDiagnostics對(duì)象的BeginRequest方法記錄相應(yīng)的診斷日志顷窒。用來真正處理當(dāng)前請(qǐng)求的ProcessRequestAsync方法比較簡單,只需要調(diào)用代表中間件委托鏈的RequestDelegate對(duì)象即可源哩。

對(duì)于用來釋放上下文的DisposeContext方法來說鞋吉,它會(huì)利用IHttpContextFactory對(duì)象的Dispose方法來釋放創(chuàng)建的HttpContext對(duì)象。換句話說励烦,HttpContext上下文的生命周期是由HostingApplication對(duì)象控制的谓着。完成針對(duì)HttpContext上下文的釋放之后,HostingApplication對(duì)象會(huì)利用HostingApplicationDiagnostics對(duì)象記錄相應(yīng)的診斷日志坛掠。Context的Scope屬性表示的日志范圍就是在調(diào)用HostingApplicationDiagnostics對(duì)象的ContextDisposed方法時(shí)釋放的赊锚。如果將HostingApplication對(duì)象引入ASP.NET Core的請(qǐng)求處理管道,那么完整的管道就體現(xiàn)為下圖所示的結(jié)構(gòu)屉栓。

三舷蒲、應(yīng)用生命周期和請(qǐng)求日志

很多人可能對(duì)ASP.NET Core框架自身記錄的診斷日志并不關(guān)心,其實(shí)很多時(shí)候這些日志對(duì)糾錯(cuò)排錯(cuò)和性能監(jiān)控提供了很有用的信息友多。例如牲平,假設(shè)需要?jiǎng)?chuàng)建一個(gè)APM(Application Performance Management)來監(jiān)控ASP.NET Core處理請(qǐng)求的性能及出現(xiàn)的異常,那么我們完全可以將HostingApplication對(duì)象記錄的日志作為收集的原始數(shù)據(jù)夷陋。實(shí)際上欠拾,目前很多APM(如Elastic APM和SkyWalking APM等)針對(duì)ASP.NET Core應(yīng)用的客戶端都是利用這種方式收集請(qǐng)求調(diào)用鏈信息的。

ILogger日志

為了確定什么樣的信息會(huì)被作為診斷日志記錄下來骗绕,下面介紹一個(gè)簡單的實(shí)例藐窄,將HostingApplication對(duì)象寫入的診斷日志輸出到控制臺(tái)上。前面提及酬土,HostingApplication對(duì)象會(huì)將相同的診斷信息以3種不同的方式進(jìn)行記錄荆忍,其中包含日志系統(tǒng),所以我們可以通過注冊(cè)對(duì)應(yīng)ILoggerProvider對(duì)象的方式將日志內(nèi)容寫入對(duì)應(yīng)的輸出渠道撤缴。

整個(gè)演示實(shí)例如下面的代碼片段所示:首先通過調(diào)用IWebHostBuilder接口的ConfigureLogging方法注冊(cè)一個(gè)ConsoleLoggerProvider對(duì)象刹枉,并開啟針對(duì)日志范圍的支持。我們調(diào)用IApplicationBuilder接口的Run擴(kuò)展方法注冊(cè)了一個(gè)中間件屈呕,該中間件在處理請(qǐng)求時(shí)會(huì)利用表示當(dāng)前請(qǐng)求上下文的HttpContext對(duì)象得到與之綁定的IServiceProvider對(duì)象微宝,并進(jìn)一步從中提取出用于發(fā)送日志事件的ILogger<Program>對(duì)象,我們利用它寫入一條Information等級(jí)的日志虎眨。如果請(qǐng)求路徑為“/error”蟋软,那么該中間件會(huì)拋出一個(gè)InvalidOperationException類型的異常。

publicclass Program

{

? ? publicstaticvoid Main()

? ? {

? ? ? ? Host.CreateDefaultBuilder()

? ? ? ? ? ? .ConfigureLogging(builder => builder.AddConsole(options => options.IncludeScopes =true))

? ? ? ? ? ? .ConfigureWebHostDefaults(builder => builder

? ? ? ? ? ? ? ? .Configure(app => app.Run(context =>? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? varlogger = context.RequestServices.GetRequiredService>();

? ? ? ? ? ? ? ? ? ? logger.LogInformation($"Log for event Foobar");

? ? ? ? ? ? ? ? ? ? if(context.Request.Path ==newPathString("/error"))

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? thrownewInvalidOperationException("Manually throw exception.");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? return Task.CompletedTask;

? ? ? ? ? ? ? ? })))

? ? ? ? ? ? .Build()

? ? ? ? ? ? .Run();

? ? }

}

在啟動(dòng)程序之后嗽桩,我們利用瀏覽器采用不同的路徑(“/foobar”和“/error”)向應(yīng)用發(fā)送了兩次請(qǐng)求岳守,演示程序的控制臺(tái)上呈現(xiàn)的輸出結(jié)果如下圖所示。由于我們開啟了日志范圍的支持碌冶,所以被ConsoleLogger記錄下來的日志都會(huì)攜帶日志范圍的信息湿痢。日志范圍的唯一標(biāo)識(shí)被稱為請(qǐng)求ID(Request ID),它由當(dāng)前的連接ID和一個(gè)序列號(hào)組成扑庞。從圖13-4可以看出譬重,兩次請(qǐng)求的ID分別是“0HLO4ON65ALGG:00000001”和“0HLO4ON65ALGG:00000002”。由于采用的是長連接嫩挤,并且兩次請(qǐng)求共享同一個(gè)連接害幅,所以它們具有相同的連接ID(“0HLO4ON65ALGG”)。同一連接的多次請(qǐng)求將一個(gè)自增的序列號(hào)(“00000001”和“00000002”)作為唯一標(biāo)識(shí)岂昭。

除了用于唯一表示每個(gè)請(qǐng)求的請(qǐng)求ID以现,日志范圍承載的信息還包括請(qǐng)求指向的路徑,這也可以從圖13-4所示的輸出接口看出來约啊。另外邑遏,上述請(qǐng)求ID實(shí)際上對(duì)應(yīng)HttpContext類型的TraceIdentifier屬性。如果需要進(jìn)行跨應(yīng)用的調(diào)用鏈跟蹤恰矩,所有相關(guān)日志就可以通過共享TraceIdentifier屬性構(gòu)建整個(gè)調(diào)用鏈记盒。

publicabstractclass HttpContext

{

? ? publicabstractstringTraceIdentifier {get;set; }

? ? ...

}

對(duì)于兩次采用不同路徑的請(qǐng)求,控制臺(tái)共捕獲了7條日志外傅,其中類別為App.Program的日志是應(yīng)用程序自行寫入的纪吮,HostingApplication寫入日志的類別為“Microsoft.AspNetCore.Hosting.Diagnostics”俩檬。對(duì)于第一次請(qǐng)求的3條日志消息,第一條是在HostingApplication開始處理請(qǐng)求時(shí)寫入的碾盟,我們利用這條日志獲知請(qǐng)求的HTTP版本(HTTP/1.1)棚辽、HTTP方法(GET)和請(qǐng)求URL。對(duì)于包含主體內(nèi)容的請(qǐng)求冰肴,請(qǐng)求主體內(nèi)容的媒體類型(Content-Type)和大星辍(Content-Length)也會(huì)一并記錄下來。當(dāng)HostingApplication對(duì)象處理完請(qǐng)求后會(huì)寫入第三條日志熙尉,日志承載的信息包括請(qǐng)求處理耗時(shí)(67.877 6毫秒)和響應(yīng)狀態(tài)碼(200)联逻。如果響應(yīng)具有主體內(nèi)容,對(duì)應(yīng)的媒體類型同樣會(huì)被記錄下來检痰。

對(duì)于第二次請(qǐng)求包归,由于我們?nèi)藶閽伋隽艘粋€(gè)異常,所以異常的信息被寫入日志攀细。但是如果足夠仔細(xì)箫踩,就會(huì)發(fā)現(xiàn)這條等級(jí)為Error的日志并不是由HostingApplication對(duì)象寫入的,而是作為服務(wù)器的KestrelServer寫入的谭贪,因?yàn)樵撊罩静捎玫念悇e為“Microsoft.AspNetCore.Server.Kestrel”境钟。換句話說,HostingApplication對(duì)象利用ILogger記錄的日志中并不包含應(yīng)用的異常信息俭识。

DiagnosticSource診斷日志

HostingApplication采用的3種日志形式還包括基于DiagnosticSource對(duì)象的診斷日志慨削,所以我們可以通過注冊(cè)診斷監(jiān)聽器來收集診斷信息。如果通過這種方式獲取診斷信息套媚,就需要預(yù)先知道診斷日志事件的名稱和內(nèi)容荷載的數(shù)據(jù)結(jié)構(gòu)缚态。通過查看HostingApplication類型的源代碼,我們會(huì)發(fā)現(xiàn)它針對(duì)“開始請(qǐng)求”堤瘤、“結(jié)束請(qǐng)求”和“未處理異趁德”這3類診斷日志事件對(duì)應(yīng)的名稱,具體如下本辐。

開始請(qǐng)求:Microsoft.AspNetCore.Hosting.BeginRequest桥帆。

結(jié)束請(qǐng)求:Microsoft.AspNetCore.Hosting.EndRequest。

未處理異常:Microsoft.AspNetCore.Hosting.UnhandledException慎皱。

至于針對(duì)診斷日志消息的內(nèi)容荷載(Payload)的結(jié)構(gòu)老虫,上述3類診斷事件具有兩個(gè)相同的成員,分別是表示當(dāng)前請(qǐng)求上下文的HttpContext和通過一個(gè)Int64整數(shù)表示的當(dāng)前時(shí)間戳茫多,對(duì)應(yīng)的數(shù)據(jù)成員的名稱分別為httpContext和timestamp祈匙。對(duì)于未處理異常診斷事件,它承載的內(nèi)容荷載還包括一個(gè)額外的成員,那就是表示拋出異常的Exception對(duì)象夺欲,對(duì)應(yīng)的成員名稱為exception跪帝。

既然我們已經(jīng)知道事件的名稱和診斷承載數(shù)據(jù)的成員,所以可以定義如下所示的DiagnosticCollector類型作為診斷監(jiān)聽器(需要針對(duì)NuGet包“Microsoft.Extensions.DiagnosticAdapter”的引用)些阅。針對(duì)上述3類診斷事件歉甚,我們?cè)贒iagnosticCollector類型中定義了3個(gè)對(duì)應(yīng)的方法,各個(gè)方法通過標(biāo)注的DiagnosticNameAttribute特性設(shè)置了對(duì)應(yīng)的診斷事件扑眉。我們根據(jù)診斷數(shù)據(jù)承載的結(jié)構(gòu)定義了匹配的參數(shù),所以DiagnosticSource對(duì)象寫入診斷日志提供的診斷數(shù)據(jù)將自動(dòng)綁定到對(duì)應(yīng)的參數(shù)上赖钞。

publicclass DiagnosticCollector

{

? ? [DiagnosticName("Microsoft.AspNetCore.Hosting.BeginRequest")]

? ? publicvoidOnRequestStart(HttpContext httpContext,long timestamp)

? ? {

? ? ? ? varrequest = httpContext.Request;

? ? ? ? Console.WriteLine($"\nRequest starting {request.Protocol} {request.Method} { request.Scheme}://{request.Host}{request.PathBase}{request.Path}");

? ? ? ? httpContext.Items["StartTimestamp"] = timestamp;

? ? }

? ? [DiagnosticName("Microsoft.AspNetCore.Hosting.EndRequest")]

? ? publicvoidOnRequestEnd(HttpContext httpContext,long timestamp)

? ? {

? ? ? ? varstartTimestamp =long.Parse(httpContext.Items["StartTimestamp"].ToString());

? ? ? ? vartimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;

? ? ? ? varelapsed =newTimeSpan((long)(timestampToTicks * (timestamp - startTimestamp)));

? ? ? ? Console.WriteLine($"Request finished in {elapsed.TotalMilliseconds}ms { httpContext.Response.StatusCode}");

? ? }

? ? [DiagnosticName("Microsoft.AspNetCore.Hosting.UnhandledException")]

? ? publicvoidOnException(HttpContext httpContext,long timestamp, Exception exception)

? ? {

? ? ? ? OnRequestEnd(httpContext, timestamp);

? ? ? ? Console.WriteLine($"{exception.Message}\nType:{exception.GetType()}\nStacktrace: { exception.StackTrace}");

? ? }

}

可以在針對(duì)“開始請(qǐng)求”診斷事件的OnRequestStart方法中輸出當(dāng)前請(qǐng)求的HTTP版本腰素、HTTP方法和URL。為了能夠計(jì)算整個(gè)請(qǐng)求處理的耗時(shí)雪营,我們將當(dāng)前時(shí)間戳保存在HttpContext上下文的Items集合中弓千。在針對(duì)“結(jié)束請(qǐng)求”診斷事件的OnRequestEnd方法中,我們將這個(gè)時(shí)間戳從HttpContext上下文中提取出來献起,結(jié)合當(dāng)前時(shí)間戳計(jì)算出請(qǐng)求處理耗時(shí)洋访,該耗時(shí)和響應(yīng)的狀態(tài)碼最終會(huì)被寫入控制臺(tái)。針對(duì)“未處理異城床停”診斷事件的OnException方法則在調(diào)用OnRequestEnd方法之后將異常的消息姻政、類型和跟蹤堆棧輸出到控制臺(tái)上。

如下面的代碼片段所示岂嗓,在注冊(cè)的Startup類型中汁展,我們?cè)贑onfigure方法注入DiagnosticListener服務(wù),并調(diào)用它的SubscribeWithAdapter擴(kuò)展方法將上述DiagnosticCollector對(duì)象注冊(cè)為診斷日志的訂閱者厌殉。與此同時(shí)食绿,我們調(diào)用IApplicationBuilder接口的Run擴(kuò)展方法注冊(cè)了一個(gè)中間件,該中間件會(huì)在請(qǐng)求路徑為“/error”的情況下拋出一個(gè)異常公罕。

publicclass Program

{

? ? publicstaticvoid Main()

? ? {

? ? ? ? Host.CreateDefaultBuilder()

? ? ? ? ? ? .ConfigureLogging(builder => builder.ClearProviders())

? ? ? ? ? ? .ConfigureWebHostDefaults(builder => builder.UseStartup())

? ? ? ? ? ? .Build()

? ? ? ? ? ? .Run();

? ? }

}publicclass Startup

{

? ? publicvoid Configure(IApplicationBuilder app, DiagnosticListener listener)

? ? {

? ? ? ? listener.SubscribeWithAdapter(new DiagnosticCollector());

? ? ? ? app.Run(context =>? ? ? ? {

? ? ? ? ? ? if(context.Request.Path ==newPathString("/error"))

? ? ? ? ? ? {

? ? ? ? ? ? ? ? thrownewInvalidOperationException("Manually throw exception.");

? ? ? ? ? ? }

? ? ? ? ? ? return Task.CompletedTask;

? ? ? ? });

? ? }

}

待演示實(shí)例正常啟動(dòng)后器紧,可以采用不同的路徑(“/foobar”和“/error”)對(duì)應(yīng)用程序發(fā)送兩個(gè)請(qǐng)求,服務(wù)端控制臺(tái)會(huì)以圖13-5所示的形式輸出DiagnosticCollector對(duì)象收集的診斷信息楼眷。如果我們?cè)噲D創(chuàng)建一個(gè)針對(duì)ASP.NET Core的APM框架來監(jiān)控請(qǐng)求處理的性能和出現(xiàn)的異常铲汪,可以采用這樣的方案來收集原始的診斷信息。

EventSource事件日志

除了上述兩種日志形式摩桶,HostingApplication對(duì)象針對(duì)每個(gè)請(qǐng)求的處理過程中還會(huì)利用EventSource對(duì)象發(fā)出相應(yīng)的日志事件桥状。除此之外,在啟動(dòng)和關(guān)閉應(yīng)用程序(實(shí)際上就是啟動(dòng)和關(guān)閉IWebHost對(duì)象)時(shí)硝清,同一個(gè)EventSource對(duì)象還會(huì)被使用辅斟。這個(gè)EventSource類型采用的名稱為Microsoft.AspNetCore.Hosting,上述5個(gè)日志事件對(duì)應(yīng)的名稱如下芦拿。

啟動(dòng)應(yīng)用程序:HostStart士飒。

開始處理請(qǐng)求:RequestStart查邢。

請(qǐng)求處理結(jié)束:RequestStop。

未處理異常:UnhandledException酵幕。

關(guān)閉應(yīng)用程序:HostStop扰藕。

我們可以通過如下所示的實(shí)例來演示如何利用創(chuàng)建的EventListener對(duì)象來監(jiān)聽上述5個(gè)日志事件。如下面的代碼片段所示芳撒,我們定義了派生于抽象類EventListener的DiagnosticCollector邓深。在啟動(dòng)應(yīng)用前,我們創(chuàng)建了這個(gè)DiagnosticCollector對(duì)象笔刹,并通過注冊(cè)其EventSourceCreated事件開啟了針對(duì)目標(biāo)名稱為Microsoft.AspNetCore.Hosting的EventSource的監(jiān)聽芥备。在注冊(cè)的EventWritten事件中,我們將監(jiān)聽到的事件名稱的負(fù)載內(nèi)容輸出到控制臺(tái)上舌菜。

publicclass Program

{

? ? privatesealedclass DiagnosticCollector : EventListener {}

? ? staticvoid Main()

? ? {

? ? ? ? varlistener =new DiagnosticCollector();

? ? ? ? listener.EventSourceCreated +=(sender, args) =>? ? ? ? {

? ? ? ? ? ? if(args.EventSource.Name =="Microsoft.AspNetCore.Hosting")

? ? ? ? ? ? {

? ? ? ? ? ? ? ? listener.EnableEvents(args.EventSource, EventLevel.LogAlways);

? ? ? ? ? ? }

? ? ? ? };?

? ? ? ? listener.EventWritten += (sender, args) =>? ? ? ? {

? ? ? ? ? ? Console.WriteLine(args.EventName);

? ? ? ? ? ? for(intindex =0; index < args.PayloadNames.Count; index++)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? Console.WriteLine($"\t{args.PayloadNames[index]} = {args.Payload[index]}");

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? Host.CreateDefaultBuilder()

? ? ? ? ? ? .ConfigureLogging(builder => builder.ClearProviders())

? ? ? ? ? ? .ConfigureWebHostDefaults(builder => builder

? ? ? ? ? ? ? ? .Configure(app => app.Run(context =>? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? if(context.Request.Path ==newPathString("/error"))

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? thrownewInvalidOperationException("Manually throw exception.");

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? return Task.CompletedTask;

? ? ? ? ? ? ? ? })))

? ? ? ? ? ? .Build()

? ? ? ? ? ? .Run();

? ? }

}

亞馬遜測(cè)評(píng) www.yisuping.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萌壳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子日月,更是在濱河造成了極大的恐慌袱瓮,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爱咬,死亡現(xiàn)場(chǎng)離奇詭異尺借,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)精拟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門褐望,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人串前,你說我怎么就攤上這事瘫里。” “怎么了荡碾?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵谨读,是天一觀的道長。 經(jīng)常有香客問我坛吁,道長劳殖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任拨脉,我火速辦了婚禮哆姻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玫膀。我一直安慰自己矛缨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箕昭,像睡著了一般灵妨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上落竹,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天泌霍,我揣著相機(jī)與錄音,去河邊找鬼述召。 笑死朱转,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的积暖。 我是一名探鬼主播肋拔,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼呀酸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琼梆,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤性誉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后茎杂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體错览,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年煌往,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倾哺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刽脖,死狀恐怖羞海,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情曲管,我是刑警寧澤却邓,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站院水,受9級(jí)特大地震影響腊徙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜檬某,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一撬腾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恢恼,春花似錦民傻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽初坠。三九已至,卻和暖如春彭雾,著一層夾襖步出監(jiān)牢的瞬間碟刺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工薯酝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留半沽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓吴菠,卻偏偏與公主長得像者填,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子做葵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容