ASP.NET Core 2.2 基礎知識(八)【日誌記錄】

風靈使發表於2019-02-19

ASP.NET Core 支援適用於各種內建和第三方日誌記錄提供程式的日誌記錄 API。 本文介紹瞭如何將日誌記錄 API 與內建提供程式一起使用。

新增提供程式

日誌記錄提供程式會顯示或儲存日誌。 例如,控制檯提供程式在控制檯上顯示日誌,Azure Application Insights 提供程式會將這些日誌儲存在 Azure Application Insights 中。 可通過新增多個提供程式將日誌傳送到多個目標。

要新增提供程式,請在 Program.cs 中呼叫提供程式的 Add{provider name} 擴充套件方法:

public static void Main(string[] args)
{
    var webHost = new WebHostBuilder()
        .UseKestrel()
        .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();
            logging.AddEventSourceLogger();
        })
        .UseStartup<Startup>()
        .Build();

    webHost.Run();
}

預設專案模板呼叫 CreateDefaultBuilder擴充套件方法,該操作將新增以下日誌記錄提供程式:

  • 控制檯
  • 除錯
  • EventSource(啟動位置:ASP.NET Core 2.2)
public static void Main(string[] args)
{
    BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .Build();

如果使用 CreateDefaultBuilder,則可自行選擇提供程式來替換預設提供程式。 呼叫 ClearProviders,然後新增所需的提供程式。

public static void Main(string[] args)
{
    var host = BuildWebHost(args);

    var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });

    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Seeded the database.");

    host.Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        })
        .Build();

詳細瞭解內建日誌記錄提供程式,以及本文稍後部分介紹的第三方日誌記錄提供程式

建立日誌

從 DI 中獲取 ILogger<TCategoryName> 物件。

以下控制器示例會建立 InformationWarning 日誌。 類別為 TodoApiSample.Controllers.TodoController(示例應用中 TodoController 的完全限定類名):

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILogger<TodoController> logger)
    {
        _todoRepository = todoRepository;
        _logger = logger;
    }
}
public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}

以下 Razor 頁面示例會建立“級別”為 Information 且“類別”為 TodoApiSample.Pages.AboutModel 的日誌:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
}
public void OnGet()
{
    Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
    _logger.LogInformation("Message displayed: {Message}", Message);
}

日誌“級別”代表所記錄事件的嚴重程度。 日誌“類別”是與每個日誌關聯的字串。 ILogger<T> 例項會建立“類別”為型別 T 的完全限定名稱的日誌。 本文稍後部分將更詳細地介紹級別類別

啟動時建立日誌

要將日誌寫入 Startup 類,建構函式簽名需包含 ILogger 引數:

public class Startup
{
    private readonly ILogger _logger;

    public Startup(IConfiguration configuration, ILogger<Startup> logger)
    {
        Configuration = configuration;
        _logger = logger;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // Add our repository type
        services.AddSingleton<ITodoRepository, TodoRepository>();
        _logger.LogInformation("Added TodoRepository to services");
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            _logger.LogInformation("In Development environment");
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc();
    }
}

在程式中建立日誌

要將日誌寫入 Program 類,請從 DI 獲取 ILogger 例項:

public static void Main(string[] args)
{
    var host = BuildWebHost(args);

    var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
    todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });

    var logger = host.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Seeded the database.");

    host.Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        })
        .Build();

沒有非同步記錄器方法

日誌記錄應該會很快,不值得犧牲效能來使用非同步程式碼。 如果你的日誌資料儲存很慢,請不要直接寫入它。 首先考慮將日誌訊息寫入快速儲存,售後再將其變為慢速儲存。 例如,記錄到由另一程式讀取和暫留以減快取儲的訊息佇列。

配置

日誌記錄提供程式配置由一個或多個配置提供程式提供:

  • 檔案格式(INI、JSON 和 XML)。
  • 命令列引數。
  • 環境變數。
  • 記憶體中的 .NET 物件。
  • 未加密的機密管理器儲存。
  • 加密的使用者儲存,如 Azure Key Vault
  • (已安裝或已建立的)自定義提供程式。

例如,日誌記錄配置通常由應用設定檔案的 Logging 部分提供。 以下示例顯示了典型 appsettings.Development.json 檔案的內容:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    },
    "Console":
    {
      "IncludeScopes": true
    }
  }
}

Logging 屬性可具有 LogLevel 和日誌提供程式屬性(顯示控制檯)。

Logging 下的 LogLevel 屬性指定了用於記錄所選類別的最低級別。 在本例中,SystemMicrosoft 類別在 Information 級別記錄,其他均在 Debug 級別記錄。

Logging 下的其他屬性均指定了日誌記錄提供程式。 本示例針對控制檯提供程式。 如果提供程式支援日誌作用域,則 IncludeScopes 將指示是否啟用這些域。 提供程式屬性(例如本例的 Console)也可指定 LogLevel 屬性。 提供程式下的 LogLevel 指定了該提供程式記錄的級別。

如果在 Logging.{providername}.LogLevel 中指定了級別,則這些級別將重寫 Logging.LogLevel 中設定的所有內容。

若要了解如何實現配置提供程式,請參閱 xref:fundamentals/configuration/index

日誌記錄輸出示例

使用上一部分中顯示的示例程式碼從命令列執行應用時,將在控制檯中看到日誌。 以下是控制檯輸出示例:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/todo/0
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
info: TodoApi.Controllers.TodoController[1002]
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      GetById(0) NOT FOUND
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 404
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 42.9286ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 148.889ms 404

通過向 http://localhost:5000/api/todo/0 處的示例應用發出 HTTP Get 請求來生成前述日誌。

在 Visual Studio 中執行示例應用時,“除錯”視窗中將顯示如下日誌:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:53104/api/todo/0  
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
TodoApi.Controllers.TodoController:Information: Getting item 0
TodoApi.Controllers.TodoController:Warning: GetById(0) NOT FOUND
Microsoft.AspNetCore.Mvc.StatusCodeResult:Information: Executing HttpStatusCodeResult, setting HTTP status code 404
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 152.5657ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 316.3195ms 404

由上一部分所示的 ILogger 呼叫建立的日誌是以“TodoApi.Controllers.TodoController”開頭的。 以“Microsoft”類別開頭的日誌來自 ASP.NET Core 框架程式碼。 ASP.NET Core 和應用程式程式碼使用相同的日誌記錄 API 和提供程式。

本文餘下部分將介紹有關日誌記錄的某些詳細資訊及選項。

NuGet 包

ILoggerILoggerFactory 介面位於 Microsoft.Extensions.Logging.Abstractions 中,其預設實現位於 Microsoft.Extensions.Logging 中。

日誌類別

建立 ILogger 物件後,將為其指定“類別”。 該類別包含在由此 Ilogger 例項建立的每條日誌訊息中。 類別可以是任何字串,但約定需使用類名,例如“TodoApi.Controllers.TodoController”。

使用 ILogger<T> 獲取一個 ILogger 例項,該例項使用 T 的完全限定型別名稱作為類別:

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILogger<TodoController> logger)
    {
        _todoRepository = todoRepository;
        _logger = logger;
    }
 }

要顯式指定類別,請呼叫 ILoggerFactory.CreateLogger

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILoggerFactory logger)
    {
        _todoRepository = todoRepository;
        _logger = logger.CreateLogger("TodoApiSample.Controllers.TodoController");
    }
}

ILogger<T> 相當於使用 T 的完全限定型別名稱來呼叫 CreateLogger

日誌級別

每個日誌都指定了一個 LogLevel 值。 日誌級別指示嚴重性或重要程度。 例如,可在方法正常結束時寫入 Information 日誌,在方法返回“404 找不到”狀態程式碼時寫入 Warning 日誌。

下面的程式碼會建立 InformationWarning 日誌:

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}

在上述程式碼中,第一個引數是日誌事件 ID。 第二個引數是訊息模板,其中的佔位符用於填寫剩餘方法形參提供的實參值。 稍後將在本文的訊息模板部分介紹方法引數。

在方法名稱中包含級別的日誌方法(例如 LogInformationLogWarning)是ILogger 的擴充套件方法。 這些方法會呼叫可接受 LogLevel 引數的 Log 方法。 可直接呼叫 Log 方法而不呼叫其中某個擴充套件方法,但語法相對複雜。 有關詳細資訊,請參閱 ILogger記錄器擴充套件原始碼

ASP.NET Core 定義了以下日誌級別(按嚴重性從低到高排列)。

  • 跟蹤 = 0

    有關通常僅用於除錯的資訊。 這些訊息可能包含敏感應用程式資料,因此不得在生產環境中啟用它們。 預設情況下禁用。

  • 除錯 = 1

    有關在開發和除錯中可能有用的資訊。 示例:Entering method Configure with flag set to true. 由於日誌數量過多,因此僅當執行故障排除時,才在生產中啟用 Debug 級別日誌。

  • 資訊 = 2

    用於跟蹤應用的常規流。 這些日誌通常有長期價值。 示例:Request received for path /api/todo

  • 警告 = 3

    表示應用流中的異常或意外事件。 可能包括不會中斷應用執行但仍需調查的錯誤或其他條件。 Warning 日誌級別常用於已處理的異常。 示例:FileNotFoundException for file quotes.txt.

  • 錯誤 = 4

    表示無法處理的錯誤和異常。 這些訊息指示的是當前活動或操作(例如當前 HTTP 請求)中的失敗,而不是整個應用中的失敗。 日誌訊息示例:Cannot insert record due to duplicate key violation.

  • 嚴重 = 5

    需要立即關注的失敗。 例如資料丟失、磁碟空間不足。

使用日誌級別控制寫入到特定儲存介質或顯示視窗的日誌輸出量。 例如:

  • 在生產中,通過 Information 級別將 Trace 傳送到卷資料儲存。 通過 Critical 級別將 Warning 傳送到值資料儲存。
  • 在開發過程中,通過Critical 級別將 Warning 傳送到控制檯,並在進行故障排除時通過 Information 級別新增 Trace

本文稍後的日誌篩選部分介紹如何控制提供程式處理的日誌級別。

ASP.NET Core 為框架事件寫入日誌。 本文前面部分提供的日誌示例排除了低於 Information 級別的日誌,因此未建立 DebugTrace 級別日誌。 以下示例介紹了通過執行配置為顯示 Debug 日誌的示例應用而生成的控制檯日誌:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:62555/api/todo/0
dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1]
      Request successfully matched the route with name 'GetTodo' and template 'api/Todo/{id}'.
dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2]
      Action 'TodoApi.Controllers.TodoController.Update (TodoApi)' with id '089d59b6-92ec-472d-b552-cc613dfd625d' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint'
dbug: Microsoft.AspNetCore.Mvc.Internal.ActionSelector[2]
      Action 'TodoApi.Controllers.TodoController.Delete (TodoApi)' with id 'f3476abe-4bd9-4ad3-9261-3ead09607366' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint'
dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action TodoApi.Controllers.TodoController.GetById (TodoApi)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method TodoApi.Controllers.TodoController.GetById (TodoApi) with arguments (0) - ModelState is Valid
info: TodoApi.Controllers.TodoController[1002]
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      GetById(0) NOT FOUND
dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action method TodoApi.Controllers.TodoController.GetById (TodoApi), returned result Microsoft.AspNetCore.Mvc.NotFoundResult.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 404
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action TodoApi.Controllers.TodoController.GetById (TodoApi) in 0.8788ms
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
      Connection id "0HL6L7NEFF2QD" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 2.7286ms 404

日誌事件 ID

每個日誌都可指定一個事件 ID。 該示例應用通過使用本地定義的 LoggingEvents 類來執行此操作:

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}
public class LoggingEvents
{
    public const int GenerateItems = 1000;
    public const int ListItems = 1001;
    public const int GetItem = 1002;
    public const int InsertItem = 1003;
    public const int UpdateItem = 1004;
    public const int DeleteItem = 1005;

    public const int GetItemNotFound = 4000;
    public const int UpdateItemNotFound = 4001;
}

事件 ID 與一組事件相關聯。 例如,與在頁面上顯示項列表相關的所有日誌可能是 1001。

日誌記錄提供程式可將事件 ID 儲存在 ID 欄位中,儲存在日誌記錄訊息中,或者不進行儲存。 除錯提供程式不顯示事件 ID。 控制檯提供程式在類別後的括號中顯示事件 ID:

info: TodoApi.Controllers.TodoController[1002]
      Getting item invalidid
warn: TodoApi.Controllers.TodoController[4000]
      GetById(invalidid) NOT FOUND

日誌訊息模板

每個日誌都會指定一個訊息模板。 訊息模板可包含要填寫引數的佔位符。 請在佔位符中使用名稱而不是數字。

public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}

佔位符的順序(而非其名稱)決定了為其提供值的引數。 在以下程式碼中,請注意訊息模板中的引數名稱未按順序排列:

string p1 = "parm1";
string p2 = "parm2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

此程式碼建立了一個引數值按順序排列的日誌訊息:

Parameter values: parm1, parm2

日誌記錄框架按此方式工作,這樣日誌記錄提供程式即可實現語義日誌記錄,也稱為結構化日誌記錄。 引數本身會傳遞給日誌記錄系統,而不僅僅是格式化的訊息模板。 通過此資訊,日誌記錄提供程式能夠將引數值儲存為欄位。 例如,假設記錄器方法呼叫如下所示:

_logger.LogInformation("Getting item {ID} at {RequestTime}", id, DateTime.Now);

如果要將日誌傳送到 Azure 表儲存,則每個 Azure 表實體都可具有 IDRequestTime 屬性,這簡化了對日誌資料的查詢。 無需分析文字訊息的超時,查詢即可找到特定 RequestTime 範圍內的全部日誌。

日誌記錄異常

記錄器方法有可傳入異常的過載,如下方示例所示:

catch (Exception ex)
{
    _logger.LogWarning(LoggingEvents.GetItemNotFound, ex, "GetById({ID}) NOT FOUND", id);
    return NotFound();
}
return new ObjectResult(item);

不同的提供程式處理異常資訊的方式不同。 以下是上示程式碼的除錯提供程式輸出示例。

TodoApi.Controllers.TodoController:Warning: GetById(036dd898-fb01-47e8-9a65-f92eb73cf924) NOT FOUND

System.Exception: Item not found exception.
 at TodoApi.Controllers.TodoController.GetById(String id) in C:\logging\sample\src\TodoApi\Controllers\TodoController.cs:line 226

日誌篩選

可為特定或所有提供程式和類別指定最低日誌級別。 最低階別以下的日誌不會傳遞給該提供程式,因此不會顯示或儲存它們。

要禁止顯示所有日誌,可將 LogLevel.None 指定為最低日誌級別。 LogLevel.None 的整數值為 6,它大於 LogLevel.Critical

在配置中建立篩選規則

專案模板程式碼呼叫 CreateDefaultBuilder 來為控制檯和除錯提供程式設定日誌記錄。 CreateDefaultBuilder 方法還使用如下所示的程式碼,設定日誌記錄以查詢 Logging 部分的配置:

public static void Main(string[] args)
{
    var webHost = new WebHostBuilder()
        .UseKestrel()
        .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();
            logging.AddEventSourceLogger();
        })
        .UseStartup<Startup>()
        .Build();

    webHost.Run();
}

配置資料按提供程式和類別指定最低日誌級別,如下方示例所示:

{
  "Logging": {
    "Debug": {
      "LogLevel": {
        "Default": "Information"
      }
    },
    "Console": {
      "IncludeScopes": false,
      "LogLevel": {
        "Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
        "Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
        "Microsoft.AspNetCore.Mvc.Razor": "Error",
        "Default": "Information"
      }
    },
    "LogLevel": {
      "Default": "Debug"
    }
  }
}

此 JSON 將建立 6 條篩選規則:1 條用於除錯提供程式, 4 條用於控制檯提供程式, 1 條用於所有提供程式。 建立 ILogger 物件時,為每個提供程式選擇一個規則。

程式碼中的篩選規則

下面的示例演示瞭如何在程式碼中註冊篩選規則:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logging =>
        logging.AddFilter("System", LogLevel.Debug)
               .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace))
    .Build();

第二個 AddFilter 使用型別名稱來指定除錯提供程式。 第一個 AddFilter 應用於全部提供程式,因為它未指定提供程式型別。

如何應用篩選規則

先前示例中顯示的配置資料和 AddFilter 程式碼會建立下表所示的規則。 前六條由配置示例建立,後兩條由程式碼示例建立。

數字 提供程式 類別的開頭為… 最低日誌級別
1 除錯 全部類別 資訊
2 控制檯 Microsoft.AspNetCore.Mvc.Razor.Internal 警告
3 控制檯 Microsoft.AspNetCore.Mvc.Razor.Razor 除錯
4 控制檯 Microsoft.AspNetCore.Mvc.Razor Error
5 控制檯 全部類別 資訊
6 全部提供程式 全部類別 除錯
7 全部提供程式 系統 除錯
8 除錯 Microsoft 跟蹤

建立 ILogger 物件時,ILoggerFactory 物件將根據提供程式選擇一條規則,將其應用於該記錄器。 將按所選規則篩選 ILogger 例項寫入的所有訊息。 從可用規則中為每個提供程式和類別對選擇最具體的規則。

在為給定的類別建立 ILogger 時,以下演算法將用於每個提供程式:

  • 選擇匹配提供程式或其別名的所有規則。 如果找不到任何匹配項,則選擇提供程式為空的所有規則。
  • 根據上一步的結果,選擇具有最長匹配類別字首的規則。 如果找不到任何匹配項,則選擇未指定類別的所有規則。
  • 如果選擇了多條規則,則採用最後一條。
  • 如果未選擇任何規則,則使用 MinimumLevel

假設你使用上述規則列表為類別“Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine”建立了 ILogger 物件:

  • 對於除錯提供程式,規則 1、6 和 8 適用。 規則 8 最為具體,因此選擇了它。
  • 對於控制檯提供程式,符合的有規則 3、規則 4、規則 5 和規則 6。 規則 3 最為具體。

生成的 ILogger 例項將 Trace 級別及更高階別的日誌傳送到除錯提供程式。 Debug 級別及更高階別的日誌會傳送到控制檯提供程式。

提供程式別名

每個提供程式都定義了一個別名;可在配置中使用該別名來代替完全限定的型別名稱。 對於內建提供程式,請使用以下別名:

  • 控制檯
  • 除錯
  • EventLog
  • AzureAppServices
  • TraceSource
  • EventSource

預設最低階別

僅當配置或程式碼中的規則對給定提供程式和類別都不適用時,最低階別設定才會生效。 下面的示例演示如何設定最低階別:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning))
    .Build();

如果沒有明確設定最低階別,則預設值為 Information,它表示 TraceDebug 日誌將被忽略。

篩選器函式

對配置或程式碼沒有向其分配規則的所有提供程式和類別呼叫篩選器函式。 函式中的程式碼可訪問提供程式型別、類別和日誌級別。 例如:

WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .ConfigureLogging(logBuilder =>
    {
        logBuilder.AddFilter((provider, category, logLevel) =>
        {
            if (provider == "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider" && 
                category == "TodoApiSample.Controllers.TodoController")
            {
                return false;
            }
            return true;
        });
    })
    .Build();

系統類別和級別

下面是 ASP.NET Core 和 Entity Framework Core 使用的一些類別,備註中說明了可從這些類別獲取的具體日誌:

類別 說明
Microsoft.AspNetCore 常規 ASP.NET Core 診斷。
Microsoft.AspNetCore.DataProtection 考慮、找到並使用了哪些金鑰。
Microsoft.AspNetCore.HostFiltering 所允許的主機。
Microsoft.AspNetCore.Hosting HTTP 請求完成的時間和啟動時間。 載入了哪些承載啟動程式集。
Microsoft.AspNetCore.Mvc MVC 和 Razor 診斷。 模型繫結、篩選器執行、檢視編譯和操作選擇。
Microsoft.AspNetCore.Routing 路由匹配資訊。
Microsoft.AspNetCore.Server 連線啟動、停止和保持活動響應。 HTTP 證照資訊。
Microsoft.AspNetCore.StaticFiles 提供的檔案。
Microsoft.EntityFrameworkCore 常規 Entity Framework Core 診斷。 資料庫活動和配置、更改檢測、遷移。

日誌作用域

“作用域”可對一組邏輯操作分組。 此分組可用於將相同的資料附加到作為集合的一部分而建立的每個日誌。 例如,在處理事務期間建立的每個日誌都可包括事務 ID。

範圍是由 BeginScope方法返回的 IDisposable 型別,持續至釋放為止。 要使用作用域,請在 using 塊中包裝記錄器呼叫:

public IActionResult GetById(string id)
{
    TodoItem item;
    using (_logger.BeginScope("Message attached to logs created in the using block"))
    {
        _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
        item = _todoRepository.Find(id);
        if (item == null)
        {
            _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
            return NotFound();
        }
    }
    return new ObjectResult(item);
}

下列程式碼為控制檯提供程式啟用作用域:

Program.cs:

.ConfigureLogging((hostingContext, logging) =>
{
    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole(options => options.IncludeScopes = true);
    logging.AddDebug();
})

備註
要啟用基於作用域的日誌記錄,必須先配置 IncludeScopes 控制檯記錄器選項。

若要了解關配置,請參閱配置部分。

每條日誌訊息都包含作用域內的資訊:

info: TodoApi.Controllers.TodoController[1002]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      Getting item 0
warn: TodoApi.Controllers.TodoController[4000]
      => RequestId:0HKV9C49II9CK RequestPath:/api/todo/0 => TodoApi.Controllers.TodoController.GetById (TodoApi) => Message attached to logs created in the using block
      GetById(0) NOT FOUND

內建日誌記錄提供程式

ASP.NET Core 提供以下提供程式:

  • 控制檯
  • 除錯
  • EventSource
  • EventLog
  • TraceSource

本文稍後將介紹 Azure 的日誌記錄選項。

有關 stdout 日誌記錄的資訊,請參閱 對 IIS 上的 ASP.NET Core 進行故障排除對 Azure 應用服務上的 ASP.NET Core 進行故障排除

控制檯提供程式

Microsoft.Extensions.Logging.Console 提供程式包向控制檯傳送日誌輸出。

logging.AddConsole();

要檢視控制檯日誌記錄輸出,請在專案資料夾中開啟命令提示符並執行以下命令:

dotnet run

除錯提供程式

Microsoft.Extensions.Logging.Debug 提供程式包使用System.Diagnostics.Debug類(Debug.WriteLine 方法呼叫)來寫入日誌輸出。

在 Linux 中,此提供程式將日誌寫入 /var/log/message。

logging.AddDebug();

EventSource 提供程式

對於面向 ASP.NET Core 1.1.0 或更高版本的應用,Microsoft.Extensions.Logging.EventSource 提供程式包可實現事件跟蹤。 在 Windows 中,它使用 ETW。 提供程式可跨平臺使用,但尚無支援 Linux 或 macOS 的事件集合和顯示工具。

logging.AddEventSourceLogger();

可使用 PerfView 實用工具收集和檢視日誌。 雖然其他工具也可以檢視 ETW 日誌,但在處理由 ASP.NET 發出的 ETW 事件時,使用 PerfView 能獲得最佳體驗。

要將 PerfView 配置為收集此提供程式記錄的事件,請向 Additional Providers 列表新增字串 *Microsoft-Extensions-Logging。 (請勿遺漏字串起始處的星號。)
其他 Perfview 提供程式

Windows EventLog 提供程式

Microsoft.Extensions.Logging.EventLog 提供程式包向 Windows 事件日誌傳送日誌輸出。

logging.AddEventLog();

TraceSource 提供程式

Microsoft.Extensions.Logging.TraceSource 提供程式包使用 TraceSource 庫和提供程式。

logging.AddTraceSource(sourceSwitchName);

AddTraceSource 過載 允許傳入資源開關和跟蹤偵聽器。

要使用此提供程式,應用必須在 .NET Framework(而非 .NET Core)上執行。 提供程式可將訊息路由到多個偵聽器,例如示例應用中使用的 TextWriterTraceListener

Azure 中的日誌記錄

要了解 Azure 中的日誌記錄,請參閱下列部分:

  • Azure 應用服務提供程式
  • Azure 日誌流式處理
  • Azure Application Insights 跟蹤日誌記錄

Azure 應用服務提供程式

Microsoft.Extensions.Logging.AzureAppServices 提供程式包將日誌寫入 Azure App Service 應用的檔案系統,以及 Azure 儲存帳戶中的 blob 儲存。 面向 .NET Core 1.1 或更高版本的應用可使用該提供程式包。

如果面向 .NET Core,請注意以下幾點:

  • Microsoft.AspNetCore.App 元包中不包括此提供程式包。 要使用提供程式,請安裝該程式包。

  • 不要顯式呼叫AddAzureWebAppDiagnostics。 將應用部署到 Azure 應用服務時,會自動使提供程式對應用可用。

如果面向 .NET Framework 或引用 Microsoft.AspNetCore.App 元包,請向專案新增提供程式包。 在 ILoggerFactory例項上呼叫 AddAzureWebAppDiagnostics

logging.AddAzureWebAppDiagnostics();

AddAzureWebAppDiagnostics過載允許傳入 AzureAppServicesDiagnosticsSettings。 設定物件可以覆蓋預設設定,例如日誌記錄輸出模板、blob 名稱和檔案大小限制。 (“輸出模板”是一個訊息模板,除了通過 ILogger 方法呼叫提供的內容之外,還可將其應用於所有日誌。)

在部署應用服務應用時,應用程式將遵循 Azure 門戶中“應用服務”頁面下的診斷日誌部分的設定。 更新這些設定後,更改會立即生效,無需重新啟動或重新部署應用。
在這裡插入圖片描述
日誌檔案的預設位置是 D:\home\LogFiles\Application 資料夾,預設檔名為 diagnostics-yyyymmdd.txt。 預設檔案大小上限為 10 MB,預設最大保留檔案數為 2。 預設 blob 名為 {app-name}{timestamp}/yyyy/mm/dd/hh/{guid}-applicationLog.txt。 要詳細瞭解預設行為,請參閱 AzureAppServicesDiagnosticsSettings

該提供程式僅當專案在 Azure 環境中執行時有效。 專案在本地執行時,該提供程式無效 — 它不會寫入本地檔案或 blob 的本地開發儲存。

Azure 日誌流式處理

通過 Azure 日誌流式處理,可從以下位置實時檢視日誌活動:

  • 應用伺服器
  • Web 伺服器
  • 請求跟蹤失敗

要配置 Azure 日誌流式處理,請執行以下操作:

  • 從應用的門戶頁導航到“診斷日誌”頁。
  • 將“應用程式日誌記錄(Filesystem)”設定為“開”。
    在這裡插入圖片描述
    導航到“日誌流式處理”頁,檢視應用訊息。 它們由應用通過 ILogger 介面記錄。
    在這裡插入圖片描述

Azure Application Insights 跟蹤日誌記錄

Application Insights SDK 可收集和報告 ASP.NET Core 日誌記錄基礎結構生成的日誌。 有關更多資訊,請參見以下資源:

第三方日誌記錄提供程式

適用於 ASP.NET Core 的第三方日誌記錄框架:

某些第三方框架可以執行語義日誌記錄(又稱結構化日誌記錄)

使用第三方框架類似於使用以下內建提供程式之一:

  1. 將 NuGet 包新增到你的專案。
  2. 呼叫 ILoggerFactory

有關詳細資訊,請參閱各提供程式的相關文件。 Microsoft 不支援第三方日誌記錄提供程式。

相關文章