ASP.NET Core 中的日志記錄

2019-04-17 08:59 更新

添加提供程序

日志記錄提供程序會(huì)顯示或存儲(chǔ)日志。 例如,控制臺(tái)提供程序在控制臺(tái)上顯示日志,Azure Application Insights 提供程序會(huì)將這些日志存儲(chǔ)在 Azure Application Insights 中。 可通過(guò)添加多個(gè)提供程序?qū)⑷罩景l(fā)送到多個(gè)目標(biāo)。

要添加提供程序,請(qǐng)?jiān)?Program.cs 中調(diào)用提供程序的 Add{provider name} 擴(kuò)展方法:

C#

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();
}

默認(rèn)項(xiàng)目模板調(diào)用 CreateDefaultBuilder,該操作將添加以下日志記錄提供程序:

  • 控制臺(tái)
  • 調(diào)試
  • EventSource(啟動(dòng)位置:ASP.NET Core 2.2)

C#

public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();

如果使用 CreateDefaultBuilder,則可自行選擇提供程序來(lái)替換默認(rèn)提供程序。 調(diào)用 ClearProviders,然后添加所需的提供程序。

C#

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    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 IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        });

詳細(xì)了解內(nèi)置日志記錄提供程序,以及本文稍后部分介紹的第三方日志記錄提供程序

創(chuàng)建日志

從 DI 中獲取 ILogger<TCategoryName> 對(duì)象。

以下控制器示例會(huì)創(chuàng)建 Information 和 Warning 日志。 類(lèi)別為 TodoApiSample.Controllers.TodoController(示例應(yīng)用中 TodoController 的完全限定類(lèi)名):

C#

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

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

C#

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 頁(yè)面示例會(huì)創(chuàng)建“級(jí)別”為 Information 且“類(lèi)別”為 TodoApiSample.Pages.AboutModel 的日志:

C#

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }

C#

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

日志“級(jí)別”代表所記錄事件的嚴(yán)重程度。 日志“類(lèi)別”是與每個(gè)日志關(guān)聯(lián)的字符串。 ILogger<T> 實(shí)例會(huì)創(chuàng)建“類(lèi)別”為類(lèi)型 T 的完全限定名稱的日志。 本文稍后部分將更詳細(xì)地介紹級(jí)別類(lèi)別。

啟動(dòng)時(shí)創(chuàng)建日志

要將日志寫(xiě)入 Startup 類(lèi),構(gòu)造函數(shù)簽名需包含 ILogger 參數(shù):

C#

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()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

        // 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();
    }
}

在程序中創(chuàng)建日志

要將日志寫(xiě)入 Program 類(lèi),請(qǐng)從 DI 獲取 ILogger 實(shí)例:

C#

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    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 IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        });

沒(méi)有異步記錄器方法

日志記錄應(yīng)該會(huì)很快,不值得犧牲性能來(lái)使用異步代碼。 如果你的日志數(shù)據(jù)存儲(chǔ)很慢,請(qǐng)不要直接寫(xiě)入它。 首先考慮將日志消息寫(xiě)入快速存儲(chǔ),售后再將其變?yōu)槁俅鎯?chǔ)。 例如,如果你要記錄到 SQL Server,你可能不想直接在 Log 方法中記錄,因?yàn)?nbsp;Log 方法是同步的。 相反,你會(huì)將日志消息同步添加到內(nèi)存中的隊(duì)列,并讓后臺(tái)輔助線程從隊(duì)列中拉出消息,以完成將數(shù)據(jù)推送到 SQL Server 的異步工作。

Configuration

日志記錄提供程序配置由一個(gè)或多個(gè)配置提供程序提供:

  • 文件格式(INI、JSON 和 XML)。
  • 命令行參數(shù)。
  • 環(huán)境變量。
  • 內(nèi)存中的 .NET 對(duì)象。
  • 未加密的機(jī)密管理器存儲(chǔ)。
  • 加密的用戶存儲(chǔ),如 Azure Key Vault。
  • (已安裝或已創(chuàng)建的)自定義提供程序。

例如,日志記錄配置通常由應(yīng)用設(shè)置文件的 Logging 部分提供。 以下示例顯示了典型 appsettings.Development.json 文件的內(nèi)容:

JSON

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

Logging 屬性可具有 LogLevel 和日志提供程序?qū)傩裕@示控制臺(tái))。

Logging 下的 LogLevel 屬性指定了用于記錄所選類(lèi)別的最低級(jí)別。 在本例中,System 和 Microsoft 類(lèi)別在 Information 級(jí)別記錄,其他均在 Debug 級(jí)別記錄。

Logging 下的其他屬性均指定了日志記錄提供程序。 本示例針對(duì)控制臺(tái)提供程序。 如果提供程序支持日志作用域,則 IncludeScopes 將指示是否啟用這些域。 提供程序?qū)傩裕ɡ绫纠?nbsp;Console)也可指定 LogLevel 屬性。 LogLevel 在提供程序下指定要為該提供程序記錄的級(jí)別。

如果在 Logging.{providername}.LogLevel 中指定了級(jí)別,則這些級(jí)別將重寫(xiě) Logging.LogLevel 中設(shè)置的所有內(nèi)容。

若要了解如何實(shí)現(xiàn)配置提供程序,請(qǐng)參閱 ASP.NET Core 中的配置

日志記錄輸出示例

使用上一部分中顯示的示例代碼從命令行運(yùn)行應(yīng)用時(shí),將在控制臺(tái)中看到日志。 以下是控制臺(tái)輸出示例:

console

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

通過(guò)向 http://localhost:5000/api/todo/0 處的示例應(yīng)用發(fā)出 HTTP Get 請(qǐng)求來(lái)生成前述日志。

在 Visual Studio 中運(yùn)行示例應(yīng)用時(shí),“調(diào)試”窗口中將顯示如下日志:

console

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 調(diào)用創(chuàng)建的日志是以“TodoApi.Controllers.TodoController”開(kāi)頭的。以“Microsoft”類(lèi)別開(kāi)頭的日志來(lái)自 ASP.NET Core 框架代碼。 ASP.NET Core 和應(yīng)用程序代碼使用相同的日志記錄 API 和提供程序。

本文余下部分將介紹有關(guān)日志記錄的某些詳細(xì)信息及選項(xiàng)。

NuGet 包

ILogger 和 ILoggerFactory 接口位于 Microsoft.Extensions.Logging.Abstractions 中,其默認(rèn)實(shí)現(xiàn)位于 Microsoft.Extensions.Logging 中。

日志類(lèi)別

創(chuàng)建 ILogger 對(duì)象后,將為其指定“類(lèi)別”。 該類(lèi)別包含在由此 ILogger 實(shí)例創(chuàng)建的每條日志消息中。 類(lèi)別可以是任何字符串,但約定需使用類(lèi)名,例如“TodoApi.Controllers.TodoController”。

使用 ILogger<T> 獲取一個(gè) ILogger 實(shí)例,該實(shí)例使用 T 的完全限定類(lèi)型名稱作為類(lèi)別:

C#

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

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

要顯式指定類(lèi)別,請(qǐng)調(diào)用 ILoggerFactory.CreateLogger:

C#

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> 相當(dāng)于調(diào)用完全限定類(lèi)型名稱為 T 的 CreateLogger。

日志級(jí)別

每個(gè)日志都指定了一個(gè) LogLevel 值。 日志級(jí)別指示嚴(yán)重性或重要程度。 例如,可在方法正常結(jié)束時(shí)寫(xiě)入 Information 日志,在方法返回“404 找不到”狀態(tài)代碼時(shí)寫(xiě)入 Warning 日志。

下面的代碼會(huì)創(chuàng)建 Information 和 Warning 日志:

C#

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);
}

在上述代碼中,第一個(gè)參數(shù)是日志事件 ID。 第二個(gè)參數(shù)是消息模板,其中的占位符用于填寫(xiě)剩余方法形參提供的實(shí)參值。 稍后將在本文的消息模板部分介紹方法參數(shù)。

在方法名稱中包含級(jí)別的日志方法(例如 LogInformation 和 LogWarning)是 ILogger 的擴(kuò)展方法。 這些方法會(huì)調(diào)用可接受 LogLevel 參數(shù)的 Log 方法。 可直接調(diào)用 Log 方法而不調(diào)用其中某個(gè)擴(kuò)展方法,但語(yǔ)法相對(duì)復(fù)雜。 有關(guān)詳細(xì)信息,請(qǐng)參閱 ILogger 和記錄器擴(kuò)展源代碼。

ASP.NET Core 定義了以下日志級(jí)別(按嚴(yán)重性從低到高排列)。

  • 跟蹤 = 0有關(guān)通常僅用于調(diào)試的信息。 這些消息可能包含敏感應(yīng)用程序數(shù)據(jù),因此不得在生產(chǎn)環(huán)境中啟用它們。 默認(rèn)情況下已禁用。
  • 調(diào)試 = 1有關(guān)在開(kāi)發(fā)和調(diào)試中可能有用的信息。 示例:Entering method Configure with flag set to true.由于日志數(shù)量過(guò)多,因此僅當(dāng)執(zhí)行故障排除時(shí),才在生產(chǎn)中啟用 Debug 級(jí)別日志。
  • 信息 = 2用于跟蹤應(yīng)用的常規(guī)流。 這些日志通常有長(zhǎng)期價(jià)值。 示例: Request received for path /api/todo
  • 警告 = 3表示應(yīng)用流中的異?;蛞馔馐录?nbsp;可能包括不會(huì)中斷應(yīng)用運(yùn)行但仍需調(diào)查的錯(cuò)誤或其他條件。 Warning 日志級(jí)別常用于已處理的異常。 示例: FileNotFoundException for file quotes.txt.
  • 錯(cuò)誤 = 4表示無(wú)法處理的錯(cuò)誤和異常。 這些消息指示的是當(dāng)前活動(dòng)或操作(例如當(dāng)前 HTTP 請(qǐng)求)中的失敗,而不是整個(gè)應(yīng)用中的失敗。 日志消息示例: Cannot insert record due to duplicate key violation.
  • 嚴(yán)重 = 5需要立即關(guān)注的失敗。 例如數(shù)據(jù)丟失、磁盤(pán)空間不足。

使用日志級(jí)別控制寫(xiě)入到特定存儲(chǔ)介質(zhì)或顯示窗口的日志輸出量。 例如:

  • 在生產(chǎn)中,通過(guò) Information 級(jí)別將 Trace 發(fā)送到卷數(shù)據(jù)存儲(chǔ)。 通過(guò) Critical 級(jí)別將 Warning 發(fā)送到值數(shù)據(jù)存儲(chǔ)。
  • 在開(kāi)發(fā)過(guò)程中,通過(guò)Critical 級(jí)別將 Warning 發(fā)送到控制臺(tái),并在進(jìn)行故障排除時(shí)通過(guò) Information 級(jí)別添加 Trace。

本文稍后的日志篩選部分介紹如何控制提供程序處理的日志級(jí)別。

ASP.NET Core 為框架事件寫(xiě)入日志。 本文前面部分提供的日志示例排除了低于 Information 級(jí)別的日志,因此未創(chuàng)建 Debug 或 Trace 級(jí)別日志。 以下示例介紹了通過(guò)運(yùn)行配置為顯示 Debug 日志的示例應(yīng)用而生成的控制臺(tái)日志:

console

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

每個(gè)日志都可指定一個(gè)事件 ID。 該示例應(yīng)用通過(guò)使用本地定義的 LoggingEvents 類(lèi)來(lái)執(zhí)行此操作:

C#

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);
}

C#

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 與一組事件相關(guān)聯(lián)。 例如,與在頁(yè)面上顯示項(xiàng)列表相關(guān)的所有日志可能是 1001。

日志記錄提供程序可將事件 ID 存儲(chǔ)在 ID 字段中,存儲(chǔ)在日志記錄消息中,或者不進(jìn)行存儲(chǔ)。 調(diào)試提供程序不顯示事件 ID。 控制臺(tái)提供程序在類(lèi)別后的括號(hào)中顯示事件 ID:

console

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

日志消息模板

每個(gè)日志都會(huì)指定一個(gè)消息模板。 消息模板可包含要填寫(xiě)參數(shù)的占位符。 請(qǐng)?jiān)谡嘉环惺褂妹Q而不是數(shù)字。

C#

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);
}

占位符的順序(而非其名稱)決定了為其提供值的參數(shù)。 在以下代碼中,請(qǐng)注意消息模板中的參數(shù)名稱未按順序排列:

C#

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

此代碼創(chuàng)建了一個(gè)參數(shù)值按順序排列的日志消息:

Parameter values: parm1, parm2

日志記錄框架按此方式工作,這樣日志記錄提供程序即可實(shí)現(xiàn)語(yǔ)義日志記錄,也稱為結(jié)構(gòu)化日志記錄。 參數(shù)本身會(huì)傳遞給日志記錄系統(tǒng),而不僅僅是格式化的消息模板。 通過(guò)此信息,日志記錄提供程序能夠?qū)?shù)值存儲(chǔ)為字段。 例如,假設(shè)記錄器方法調(diào)用如下所示:

C#

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

如果要將日志發(fā)送到 Azure 表存儲(chǔ),則每個(gè) Azure 表實(shí)體都可具有 ID 和 RequestTime 屬性,這簡(jiǎn)化了對(duì)日志數(shù)據(jù)的查詢。 無(wú)需分析文本消息的超時(shí),查詢即可找到特定 RequestTime 范圍內(nèi)的全部日志。

日志記錄異常

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

C#

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

不同的提供程序處理異常信息的方式不同。 以下是上示代碼的調(diào)試提供程序輸出示例。

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

日志篩選

可為特定或所有提供程序和類(lèi)別指定最低日志級(jí)別。 最低級(jí)別以下的日志不會(huì)傳遞給該提供程序,因此不會(huì)顯示或存儲(chǔ)它們。

要禁止顯示所有日志,可將 LogLevel.None 指定為最低日志級(jí)別。 LogLevel.None 的整數(shù)值為 6,它大于 LogLevel.Critical (5)。

在配置中創(chuàng)建篩選規(guī)則

項(xiàng)目模板代碼調(diào)用 CreateDefaultBuilder 來(lái)為控制臺(tái)和調(diào)試提供程序設(shè)置日志記錄。 CreateDefaultBuilder 方法還使用如下所示的代碼,設(shè)置日志記錄以查找 Logging 部分的配置:

C#

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();
}

配置數(shù)據(jù)按提供程序和類(lèi)別指定最低日志級(jí)別,如下方示例所示:

JSON

{
  "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 將創(chuàng)建 6 條篩選規(guī)則:1 條用于調(diào)試提供程序, 4 條用于控制臺(tái)提供程序, 1 條用于所有提供程序。 創(chuàng)建 ILogger 對(duì)象時(shí),為每個(gè)提供程序選擇一個(gè)規(guī)則。

代碼中的篩選規(guī)則

下面的示例演示了如何在代碼中注冊(cè)篩選規(guī)則:

C#

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

第二個(gè) AddFilter 使用類(lèi)型名稱來(lái)指定調(diào)試提供程序。 第一個(gè) AddFilter 應(yīng)用于全部提供程序,因?yàn)樗粗付ㄌ峁┏绦蝾?lèi)型。

如何應(yīng)用篩選規(guī)則

先前示例中顯示的配置數(shù)據(jù)和 AddFilter 代碼會(huì)創(chuàng)建下表所示的規(guī)則。 前六條由配置示例創(chuàng)建,后兩條由代碼示例創(chuàng)建。

數(shù)字提供程序類(lèi)別的開(kāi)頭為...最低日志級(jí)別
1調(diào)試全部類(lèi)別信息
2控制臺(tái)Microsoft.AspNetCore.Mvc.Razor.Internal警告
3控制臺(tái)Microsoft.AspNetCore.Mvc.Razor.Razor調(diào)試
4控制臺(tái)Microsoft.AspNetCore.Mvc.RazorError
5控制臺(tái)全部類(lèi)別信息
6全部提供程序全部類(lèi)別調(diào)試
7全部提供程序系統(tǒng)調(diào)試
8調(diào)試Microsoft跟蹤

創(chuàng)建 ILogger 對(duì)象時(shí),ILoggerFactory 對(duì)象將根據(jù)提供程序選擇一條規(guī)則,將其應(yīng)用于該記錄器。將按所選規(guī)則篩選 ILogger 實(shí)例寫(xiě)入的所有消息。 從可用規(guī)則中為每個(gè)提供程序和類(lèi)別對(duì)選擇最具體的規(guī)則。

在為給定的類(lèi)別創(chuàng)建 ILogger 時(shí),以下算法將用于每個(gè)提供程序:

  • 選擇匹配提供程序或其別名的所有規(guī)則。 如果找不到任何匹配項(xiàng),則選擇提供程序?yàn)榭盏乃幸?guī)則。
  • 根據(jù)上一步的結(jié)果,選擇具有最長(zhǎng)匹配類(lèi)別前綴的規(guī)則。 如果找不到任何匹配項(xiàng),則選擇未指定類(lèi)別的所有規(guī)則。
  • 如果選擇了多條規(guī)則,則采用最后一條。
  • 如果未選擇任何規(guī)則,則使用 MinimumLevel。

假設(shè)你使用上述規(guī)則列表為類(lèi)別“Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine”創(chuàng)建了 ILogger 對(duì)象:

  • 對(duì)于調(diào)試提供程序,規(guī)則 1、6 和 8 適用。 規(guī)則 8 最為具體,因此選擇了它。
  • 對(duì)于控制臺(tái)提供程序,符合的有規(guī)則 3、規(guī)則 4、規(guī)則 5 和規(guī)則 6。 規(guī)則 3 最為具體。

生成的 ILogger 實(shí)例將 Trace 級(jí)別及更高級(jí)別的日志發(fā)送到調(diào)試提供程序。 Debug 級(jí)別及更高級(jí)別的日志會(huì)發(fā)送到控制臺(tái)提供程序。

提供程序別名

每個(gè)提供程序都定義了一個(gè)別名;可在配置中使用該別名來(lái)代替完全限定的類(lèi)型名稱。 對(duì)于內(nèi)置提供程序,請(qǐng)使用以下別名:

  • 控制臺(tái)
  • 調(diào)試
  • EventLog
  • AzureAppServices
  • TraceSource
  • EventSource

默認(rèn)最低級(jí)別

僅當(dāng)配置或代碼中的規(guī)則對(duì)給定提供程序和類(lèi)別都不適用時(shí),最低級(jí)別設(shè)置才會(huì)生效。 下面的示例演示如何設(shè)置最低級(jí)別:

C#

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

如果沒(méi)有明確設(shè)置最低級(jí)別,則默認(rèn)值為 Information,它表示 Trace 和 Debug 日志將被忽略。

篩選器函數(shù)

對(duì)配置或代碼沒(méi)有向其分配規(guī)則的所有提供程序和類(lèi)別調(diào)用篩選器函數(shù)。 函數(shù)中的代碼可訪問(wèn)提供程序類(lèi)型、類(lèi)別和日志級(jí)別。 例如:

C#

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;
        });
    });

系統(tǒng)類(lèi)別和級(jí)別

下面是 ASP.NET Core 和 Entity Framework Core 使用的一些類(lèi)別,備注中說(shuō)明了可從這些類(lèi)別獲取的具體日志:

類(lèi)別說(shuō)明
Microsoft.AspNetCore常規(guī) ASP.NET Core 診斷。
Microsoft.AspNetCore.DataProtection考慮、找到并使用了哪些密鑰。
Microsoft.AspNetCore.HostFiltering所允許的主機(jī)。
Microsoft.AspNetCore.HostingHTTP 請(qǐng)求完成的時(shí)間和啟動(dòng)時(shí)間。 加載了哪些承載啟動(dòng)程序集。
Microsoft.AspNetCore.MvcMVC 和 Razor 診斷。 模型綁定、篩選器執(zhí)行、視圖編譯和操作選擇。
Microsoft.AspNetCore.Routing路由匹配信息。
Microsoft.AspNetCore.Server連接啟動(dòng)、停止和保持活動(dòng)響應(yīng)。 HTTP 證書(shū)信息。
Microsoft.AspNetCore.StaticFiles提供的文件。
Microsoft.EntityFrameworkCore常規(guī) Entity Framework Core 診斷。 數(shù)據(jù)庫(kù)活動(dòng)和配置、更改檢測(cè)、遷移。

日志作用域

“作用域”可對(duì)一組邏輯操作分組。 此分組可用于將相同的數(shù)據(jù)附加到作為集合的一部分而創(chuàng)建的每個(gè)日志。 例如,在處理事務(wù)期間創(chuàng)建的每個(gè)日志都可包括事務(wù) ID。

范圍是由 BeginScope 方法返回的 IDisposable 類(lèi)型,持續(xù)至釋放為止。 要使用作用域,請(qǐng)?jiān)?nbsp;using 塊中包裝記錄器調(diào)用:

C#

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);
}

下列代碼為控制臺(tái)提供程序啟用作用域:

Program.cs:

C#

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

 備注

要啟用基于作用域的日志記錄,必須先配置 IncludeScopes 控制臺(tái)記錄器選項(xiàng)。

若要了解關(guān)配置,請(qǐng)參閱配置部分。

每條日志消息都包含作用域內(nèi)的信息:

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

內(nèi)置日志記錄提供程序

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

本文稍后將介紹 Azure 的日志記錄選項(xiàng)。

有關(guān) stdout 日志記錄的信息,請(qǐng)參閱 對(duì) IIS 上的 ASP.NET Core 進(jìn)行故障排除 和 對(duì) Azure 應(yīng)用服務(wù)上的 ASP.NET Core 進(jìn)行故障排除。

控制臺(tái)提供程序

Microsoft.Extensions.Logging.Console 提供程序包向控制臺(tái)發(fā)送日志輸出。

C#

logging.AddConsole();

要查看控制臺(tái)日志記錄輸出,請(qǐng)?jiān)陧?xiàng)目文件夾中打開(kāi)命令提示符并運(yùn)行以下命令:

console

dotnet run

調(diào)試提供程序

Microsoft.Extensions.Logging.Debug 提供程序包使用 System.Diagnostics.Debug 類(lèi)(Debug.WriteLine 方法調(diào)用)來(lái)寫(xiě)入日志輸出。

在 Linux 中,此提供程序?qū)⑷罩緦?xiě)入 /var/log/message。

C#

logging.AddDebug();

EventSource 提供程序

對(duì)于面向 ASP.NET Core 1.1.0 或更高版本的應(yīng)用,Microsoft.Extensions.Logging.EventSource 提供程序包可實(shí)現(xiàn)事件跟蹤。 在 Windows 中,它使用 ETW。 提供程序可跨平臺(tái)使用,但尚無(wú)支持 Linux 或 macOS 的事件集合和顯示工具。

C#

logging.AddEventSourceLogger();

可使用 PerfView 實(shí)用工具收集和查看日志。 雖然其他工具也可以查看 ETW 日志,但在處理由 ASP.NET 發(fā)出的 ETW 事件時(shí),使用 PerfView 能獲得最佳體驗(yàn)。

要將 PerfView 配置為收集此提供程序記錄的事件,請(qǐng)向 Additional Providers 列表添加字符串 *Microsoft-Extensions-Logging。 (請(qǐng)勿遺漏字符串起始處的星號(hào)。)

其他 Perfview 提供程序

Windows EventLog 提供程序

Microsoft.Extensions.Logging.EventLog 提供程序包向 Windows 事件日志發(fā)送日志輸出。

C#

logging.AddEventLog();

TraceSource 提供程序

Microsoft.Extensions.Logging.TraceSource 提供程序包使用 TraceSource 庫(kù)和提供程序。

C#

logging.AddTraceSource(sourceSwitchName);

AddTraceSource 重載 允許傳入資源開(kāi)關(guān)和跟蹤偵聽(tīng)器。

要使用此提供程序,應(yīng)用必須在 .NET Framework(而非 .NET Core)上運(yùn)行。 提供程序可將消息路由到多個(gè)偵聽(tīng)器,例如示例應(yīng)用中使用的 TextWriterTraceListener

Azure 中的日志記錄

要了解 Azure 中的日志記錄,請(qǐng)參閱下列部分:

Azure 應(yīng)用服務(wù)提供程序

Microsoft.Extensions.Logging.AzureAppServices 提供程序包將日志寫(xiě)入 Azure App Service 應(yīng)用的文件系統(tǒng),以及 Azure 存儲(chǔ)帳戶中的 blob 存儲(chǔ)。 面向 .NET Core 1.1 或更高版本的應(yīng)用可使用該提供程序包。

如果面向 .NET Core,請(qǐng)注意以下幾點(diǎn):

  • 不要顯式調(diào)用 AddAzureWebAppDiagnostics。 將應(yīng)用部署到 Azure 應(yīng)用服務(wù)時(shí),會(huì)自動(dòng)使提供程序?qū)?yīng)用可用。

如果面向 .NET Framework 或引用 Microsoft.AspNetCore.App 元包,請(qǐng)向項(xiàng)目添加提供程序包。 調(diào)用 AddAzureWebAppDiagnostics:

C#

logging.AddAzureWebAppDiagnostics();

要配置提供程序設(shè)置,請(qǐng)使用 AzureFileLoggerOptions 和 AzureBlobLoggerOptions,如以下示例所示:

C#

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    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 IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
        .ConfigureServices(serviceCollection => serviceCollection
                .Configure<AzureFileLoggerOptions>(options =>
                {
                    options.FileName = "azure-diagnostics-";
                    options.FileSizeLimit = 50 * 1024;
                    options.RetainedFileCountLimit = 5;
                }).Configure<AzureBlobLoggerOptions>(options =>
                {
                    options.BlobName = "log.txt";
                }))
        .UseStartup<Startup>();

在部署應(yīng)用服務(wù)應(yīng)用時(shí),應(yīng)用程序?qū)⒆裱?Azure 門(mén)戶中“應(yīng)用服務(wù)”頁(yè)面下的診斷日志部分的設(shè)置。 更新這些設(shè)置后,更改會(huì)立即生效,無(wú)需重新啟動(dòng)或重新部署應(yīng)用。

Azure 日志記錄設(shè)置

日志文件的默認(rèn)位置是 D:\home\LogFiles\Application 文件夾,默認(rèn)文件名為 diagnostics-yyyymmdd.txt。 默認(rèn)文件大小上限為 10 MB,默認(rèn)最大保留文件數(shù)為 2。 默認(rèn) blob 名為 {app-name}{timestamp}/yyyy/mm/dd/hh/{guid}-applicationLog.txt。

該提供程序僅當(dāng)項(xiàng)目在 Azure 環(huán)境中運(yùn)行時(shí)有效。 項(xiàng)目在本地運(yùn)行時(shí),該提供程序無(wú)效 — 它不會(huì)寫(xiě)入本地文件或 blob 的本地開(kāi)發(fā)存儲(chǔ)。

Azure 日志流式處理

通過(guò) Azure 日志流式處理,可從以下位置實(shí)時(shí)查看日志活動(dòng):

  • 應(yīng)用服務(wù)器
  • Web 服務(wù)器
  • 請(qǐng)求跟蹤失敗

要配置 Azure 日志流式處理,請(qǐng)執(zhí)行以下操作:

  • 從應(yīng)用的門(mén)戶頁(yè)導(dǎo)航到“診斷日志”頁(yè)。
  • 將“應(yīng)用程序日志記錄(Filesystem)”設(shè)置為“開(kāi)”。

Azure 門(mén)戶診斷日志頁(yè)

導(dǎo)航到“日志流式處理”頁(yè),查看應(yīng)用消息。 它們由應(yīng)用通過(guò) ILogger 接口記錄。

Azure 門(mén)戶應(yīng)用程序日志流式處理

Azure Application Insights 跟蹤日志記錄

Application Insights SDK 可收集和報(bào)告 ASP.NET Core 日志記錄基礎(chǔ)結(jié)構(gòu)生成的日志。 有關(guān)更多信息,請(qǐng)參見(jiàn)以下資源:

第三方日志記錄提供程序

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

某些第三方框架可以執(zhí)行語(yǔ)義日志記錄(又稱結(jié)構(gòu)化日志記錄)。

使用第三方框架類(lèi)似于使用以下內(nèi)置提供程序之一:

  1. 將 NuGet 包添加到你的項(xiàng)目。
  2. 調(diào)用 ILoggerFactory。

有關(guān)詳細(xì)信息,請(qǐng)參閱各提供程序的相關(guān)文檔。 Microsoft 不支持第三方日志記錄提供程序。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)