溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

ASP.NET Core應(yīng)用中怎么記錄和查看日志

發(fā)布時(shí)間:2021-02-18 10:53:08 來源:億速云 閱讀:199 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下ASP.NET Core應(yīng)用中怎么記錄和查看日志,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

日志記錄不僅對(duì)于我們開發(fā)的應(yīng)用,還是對(duì)于ASP.NET Core框架功能都是一項(xiàng)非常重要的功能特性。我們知道ASP.NET Core使用的是一個(gè)極具擴(kuò)展性的日志系統(tǒng),該系統(tǒng)由Logger、LoggerFactory和LoggerProvider這三個(gè)核心對(duì)象組成。我們可以通過簡(jiǎn)單的配置實(shí)現(xiàn)對(duì)LoggerFactory的定制,以及對(duì)LoggerProvider添加。

一、 配置LoggerFactory

我們?cè)谏厦嬉还?jié)演示了一個(gè)展示ASP.NET Core默認(rèn)注冊(cè)服務(wù)的實(shí)例,細(xì)心的讀者一定會(huì)看到顯示的列表中就包含了針對(duì)LoggerFactory的服務(wù)。如果這個(gè)默認(rèn)的LoggerFactory服務(wù)不能滿足我們的需求,我們完全可以配置任何一個(gè)需要的LoggerFactory,針對(duì)LoggerFactory的設(shè)置可以直接調(diào)用WebHostBuilder的UseLoggerFactory方法來實(shí)現(xiàn)。

public interface IWebHostBuilder
  {
    IWebHostBuilder UseLoggerFactory(ILoggerFactory loggerFactory);
    IWebHostBuilder ConfigureLogging(Action<ILoggerFactory> configureLogging);
    ...
  }

不過針對(duì)日志的配置更多地還是體現(xiàn)在針對(duì)某種LoggerProvider的添加,而這可以通過調(diào)用WebHostBuilder的ConfigureLogging方法來完成。我們?cè)谏厦嫜菔镜膶?shí)例中就曾經(jīng)采用如下的方式將一個(gè)ConsoleLoggerProvider注冊(cè)到LoggerFactory之上,這樣我們可以直接在宿主應(yīng)用的擴(kuò)展臺(tái)上看到記錄的日志信息。

 new WebHostBuilder()
    .ConfigureLogging(factory=>factory.AddConsole())
    ...

既然LoggerFactory已經(jīng)作為一個(gè)服務(wù)進(jìn)行了注冊(cè),那么我們完全按照依賴注入的來獲取這個(gè)對(duì)象,并利用它創(chuàng)建對(duì)應(yīng)的Logger對(duì)象來寫日志。如果我們需要在一個(gè)定義的中間件中寫入某種類型的日志,就可以按照如下的方式在Invoke方法中定義ILoggerFactory類型的參數(shù)注入這個(gè)LoggerFactory。

 public class FoobarMiddleware
  {
    private RequestDelegate _next;
   
    public FoobarMiddleware(RequestDelegate next)
    {
      _next = next;
    }
   
    public async Task Invoke(HttpContext context, ILoggerFactory loggerFactory)
    {
      ILogger<FoobarMiddleware> logger = loggerFactory.CreateLogger<FoobarMiddleware>();
      logger.LogInformation("...");
      await _next(context);
    }
  }

不僅僅我們開發(fā)的應(yīng)用或者中間件可以利用注冊(cè)的LoggerFactory來創(chuàng)建進(jìn)行日志記錄的Logger對(duì)象,ASP.NET Core管道本身也會(huì)在處理請(qǐng)求過程中采用相同的方式記錄一些日志。比如管道每次處理請(qǐng)求的開始和結(jié)束時(shí)候分別會(huì)寫入兩條Information等級(jí)的日志,我們現(xiàn)在就來通過一個(gè)簡(jiǎn)單的實(shí)例看看這兩條日志信息具有怎樣的內(nèi)容。

public class Program
  {
    public static void Main()
    {
      new WebHostBuilder()
        .ConfigureLogging(factory=>factory.AddConsole())
        .UseKestrel()
        .UseStartup<Startup>()        
        .Build()
        .Run();
    }
  }
  
  public class Startup
  {
    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
      app.Run(async context =>
      {
        loggerFactory.CreateLogger("App").LogInformation("Log entry for test...");
        await context.Response.WriteAsync("Hello world!");
      });
    }
  }

如上所示的代碼有兩處與日志有關(guān),第一個(gè)地方是調(diào)用WebHostBuilder的ConfigureLogging方法通過調(diào)用擴(kuò)展方法AddConsole將一個(gè)ConsoleProvider添加到當(dāng)前LoggerFactory之上,另一個(gè)地方就是啟動(dòng)類的Configure方法注冊(cè)的中間件在執(zhí)行過程中會(huì)利用注入的LoggerFactory創(chuàng)建一個(gè)Logger對(duì)象,我們利用后者寫入了一條Information等級(jí)的日志。我們運(yùn)行程序之后利用瀏覽器訪問目標(biāo)地址后,宿主控制臺(tái)上會(huì)出現(xiàn)如下圖所示的三條日志。除了第二條日志是由我們自己編寫的代碼寫入的之外,其余兩條都是ASP.NET Core框架自己寫入的。第一條日志包含不僅僅包含請(qǐng)求的目標(biāo)地址,還包括請(qǐng)求采用的協(xié)議(HTTP/1.1)和HTTP方法(GET),第三條則反映了整個(gè)請(qǐng)求處理過程所花的時(shí)間。

ASP.NET Core應(yīng)用中怎么記錄和查看日志

由于ASP.NET Core管道對(duì)請(qǐng)求的處理總是在一個(gè)由HttpApplication創(chuàng)建的執(zhí)行上下文中進(jìn)行,所以上下文的創(chuàng)建和回收釋放可以視為 整個(gè)請(qǐng)求處理流程開始和結(jié)束的標(biāo)識(shí)。對(duì)于上述的這兩條分別在處理請(qǐng)求開始和結(jié)束時(shí)寫入的日志,實(shí)際上是在HostingApplication的CreateContext和DisposeContext方法分別被調(diào)用的時(shí)候被記錄下來的。之所以在結(jié)束的時(shí)候能夠計(jì)算出整個(gè)請(qǐng)求處理過程所花的時(shí)間,是因?yàn)閯?chuàng)建的這個(gè)上下文對(duì)象保存了開始處理請(qǐng)求的時(shí)間戳,該時(shí)間戳對(duì)應(yīng)著Context結(jié)構(gòu)的StartTimestamp屬性。

 public class HostingApplication IHttpApplication<HostingApplication.Context>
  {
    public struct Context
    {
      public HttpContext   HttpContext { get; set; }
      public IDisposable   Scope { get; set; }
      public long     StartTimestamp { get; set; }
    }
  }

二、以當(dāng)前請(qǐng)求作為日志范圍

我們知道日志系統(tǒng)有一個(gè)叫做“日志范圍”的概念,它的目的在于為多次相關(guān)的日志記錄創(chuàng)建一個(gè)上下文范圍,并為這個(gè)范圍提供一個(gè)唯一標(biāo)識(shí),這個(gè)標(biāo)識(shí)會(huì)作為日志內(nèi)容的一部分被寫入。當(dāng)我們?cè)谶M(jìn)行日志分析的時(shí)候,可以根據(jù)日志范圍標(biāo)識(shí)將一組原本獨(dú)立的日志關(guān)聯(lián)起來。這個(gè)概念對(duì)于Web應(yīng)用尤為重要,因?yàn)楹芏嗲闆r下我們所做的日志分析都是針對(duì)某一個(gè)請(qǐng)求,這就要求我們必須明確地分辨出被記錄下來的日志隸屬于哪一個(gè)請(qǐng)求,只有這樣才能將針對(duì)同一請(qǐng)求的所有日志提取出來做綜合的分析以得出一個(gè)準(zhǔn)確的結(jié)論。

從上個(gè)實(shí)例最終寫入的三條日志來看,它們并不攜帶當(dāng)前請(qǐng)求的標(biāo)識(shí)信息。但是這不是ASP.NET Core的問題,而是我們?cè)谡{(diào)用LoggerFactory的擴(kuò)展方法AddConsole注冊(cè)ConsoleLoggerProvider的時(shí)候并未顯式開啟針對(duì)日志范圍的支持。為了讓注冊(cè)的ConsoleLoggerProvider創(chuàng)建的Logger能夠支持日志范圍,我們只需按照如下的方式在調(diào)用AddConsole方法的時(shí)候添加一個(gè)額外的參數(shù)(true)即可。

 new WebHostBuilder()
    .ConfigureLogging(factory=>factory.AddConsole(true))
    .UseKestrel()
    .UseStartup<Startup>()        
    .Build()
    .Run();

我們?cè)俅握?qǐng)求應(yīng)用并利用瀏覽器對(duì)目標(biāo)地址發(fā)送兩次請(qǐng)求,六條寫入的日志將會(huì)以如下圖所示的形式輸出到控制臺(tái)上。不同于上面的輸出結(jié)果,本次輸出的日志包含請(qǐng)求的ID(Request Id),在同一個(gè)請(qǐng)求下被記錄下來的日志具有相同的ID。除了請(qǐng)求ID,記錄的日志還攜帶了請(qǐng)求的路徑(Request Path)。

ASP.NET Core應(yīng)用中怎么記錄和查看日志

日志范圍攜帶的用于唯一標(biāo)識(shí)當(dāng)前請(qǐng)求的ID,同時(shí)也可以視為當(dāng)前HttpContext的唯一標(biāo)識(shí),它對(duì)應(yīng)著HttpContext的TranceIdentifier屬性。對(duì)于DefaultHttpContext來說,針對(duì)這個(gè)屬性的讀寫是借助一個(gè)名為HttpRequestIdentifierFeature的特性實(shí)現(xiàn)的,下面的代碼提供了該對(duì)象對(duì)應(yīng)的接口IHttpRequestIdentifierFeature和默認(rèn)實(shí)現(xiàn)類HttpRequestIdentifierFeature的定義。

public abstract class HttpContext
  {
    //省略其他成員
    public abstract string TraceIdentifier { get; set; }
  }
   
  public interface IHttpRequestIdentifierFeature
  {
    string TraceIdentifier { get; set; }
  }
  
  public class HttpRequestIdentifierFeature IHttpRequestIdentifierFeature
  {
    private string _id;
    private static long _requestId = DateTime.UtcNow.Ticks;
    private static unsafe string GenerateRequestId(long id);
    public string TraceIdentifier
    {
      get
      {
        return _id??(id= GenerateRequestId(Interlocked.Increment(ref _requestId)));
      }
      set
      {
        this._id = value;
      }
    }
  }

HttpRequestIdentifierFeature生成TraceIdentifier的邏輯很簡(jiǎn)單。如上面的代碼片斷所示,它具有一個(gè)靜態(tài)長(zhǎng)整型字段_requestId,其初始值為當(dāng)前時(shí)間戳。對(duì)于某個(gè)具體的HttpRequestIdentifierFeature對(duì)象來說,它的TraceIdentifier屬性的默認(rèn)值返回的是這個(gè)字段_requestId加1之后轉(zhuǎn)換的字符串。具體的轉(zhuǎn)換邏輯定義在GenerateRequestId方法中,它會(huì)采用相應(yīng)的算法 將指定的整數(shù)轉(zhuǎn)換一個(gè)長(zhǎng)度為13的字符串。

和開始請(qǐng)求處理的時(shí)間戳一樣,被創(chuàng)建出來的日志范圍實(shí)際被保存在HostingApplication的上下文對(duì)象中,它對(duì)應(yīng)著Context結(jié)構(gòu)的Scope屬性。當(dāng)HostingApplication創(chuàng)建這個(gè)Context對(duì)象的時(shí)候,它會(huì)從當(dāng)前HttpContext中提取出請(qǐng)求的ID和路徑,創(chuàng)建出這個(gè)日志范圍并賦值給這個(gè)屬性。整個(gè)請(qǐng)求的處理其實(shí)就在這個(gè)日志范圍中進(jìn)行,請(qǐng)求處理結(jié)束,當(dāng)前日志范文也被回收釋放。

 public class HostingApplication IHttpApplication<HostingApplication.Context>
  {
    public struct Context
    {
      public HttpContext   HttpContext { get; set; }
      public IDisposable   Scope { get; set; }
      public long      StartTimestamp { get; set; }
    }
  }

三、記錄異常日志

由于ASP.NET Core在處理請(qǐng)求過程中導(dǎo)致的異常并不會(huì)導(dǎo)致應(yīng)用終止,考慮到安全,拋出的異常的詳細(xì)信息也不應(yīng)該直接返回到客戶端。所以在很多情況下我們根本感知不到應(yīng)用發(fā)生了異常,即使感知到了,也不知道導(dǎo)致異常的根源在何處。在這種情況下,我們就需要使用記錄的日志進(jìn)行差錯(cuò)和糾錯(cuò),因?yàn)锳SP.NET Core在處理請(qǐng)求遇到的異常都會(huì)記錄到日志中。

比如針對(duì)如下這段程序,毫無疑問它針對(duì)任何一個(gè)請(qǐng)求的處理都會(huì)拋出一個(gè)DivideByZeroException的異常。如果我們利用瀏覽器來訪問站點(diǎn)地址,它只會(huì)得到一個(gè)狀態(tài)為500的響應(yīng),并簡(jiǎn)單的提示服務(wù)端出現(xiàn)錯(cuò)誤。對(duì)于宿主程序來說,我們根本就是感知不到任何異常發(fā)生。

 new WebHostBuilder()
    .UseKestrel()
    .Configure(app=>app.Run(async context=> {
      int x = 1;
      int y = 0;
      await context.Response.WriteAsync((x / y).ToString());
    }))
    .Build()
  .Run();

在這種情況下我們可以通過查看日志得到異常的詳細(xì)信息,不過在這之前必須為L(zhǎng)oggerFactory注冊(cè)相應(yīng)的LoggerProvider。如果我們采用控制臺(tái)應(yīng)用作為宿主,在開發(fā)或者調(diào)試的時(shí)候最簡(jiǎn)單的莫過于按照如下的方式注冊(cè)一個(gè)ConsoleLoggerProvider讓日志可以直接寫入宿主程序的控制臺(tái)。

 new WebHostBuilder()
    .ConfigureLogging(factory=>factory.AddConsole (true))
    .UseKestrel()
    .Configure(app=>app.Run(async context=> {
      int x = 1;
      int y = 0;
      await context.Response.WriteAsync((x / y).ToString());
    }))
    .Build()
    .Run();

一旦為L(zhǎng)oggerFactory注冊(cè)了這么一個(gè)ConsoleLoggerProvider,對(duì)于服務(wù)端出現(xiàn)的未處理的任何異常,我們都可以直接在宿主控制臺(tái)上看到錯(cuò)誤的詳細(xì)信息,下圖就是上面這個(gè)例子拋出的DivideByZeroException異常的詳細(xì)信息。

ASP.NET Core應(yīng)用中怎么記錄和查看日志

以上是“ASP.NET Core應(yīng)用中怎么記錄和查看日志”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI