ASP.NET Core 中的日志記錄

2019-04-17 08:59 更新

添加提供程序

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

要添加提供程序,請在 Program.cs 中調(diào)用提供程序的 Add{provider name} 擴展方法:

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

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

  • 控制臺
  • 調(diào)試
  • EventSource(啟動位置: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,則可自行選擇提供程序來替換默認提供程序。 調(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();
        });

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

創(chuàng)建日志

從 DI 中獲取 ILogger<TCategoryName> 對象。

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

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 頁面示例會創(chuàng)建“級別”為 Information 且“類別”為 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);
}

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

啟動時創(chuàng)建日志

要將日志寫入 Startup 類,構(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)建日志

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

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

沒有異步記錄器方法

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

Configuration

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

  • 文件格式(INI、JSON 和 XML)。
  • 命令行參數(shù)。
  • 環(huán)境變量。
  • 內(nèi)存中的 .NET 對象。
  • 未加密的機密管理器存儲。
  • 加密的用戶存儲,如 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ū)傩裕@示控制臺)。

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

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

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

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

日志記錄輸出示例

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

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

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

在 Visual Studio 中運行示例應(yīng)用時,“調(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”開頭的。以“Microsoft”類別開頭的日志來自 ASP.NET Core 框架代碼。 ASP.NET Core 和應(yīng)用程序代碼使用相同的日志記錄 API 和提供程序。

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

NuGet 包

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

日志類別

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

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

C#

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

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

要顯式指定類別,請調(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> 相當于調(diào)用完全限定類型名稱為 T 的 CreateLogger。

日志級別

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

下面的代碼會創(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);
}

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

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

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

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

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

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

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

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

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

每個日志都可指定一個事件 ID。 該示例應(yīng)用通過使用本地定義的 LoggingEvents 類來執(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)。 例如,與在頁面上顯示項列表相關(guān)的所有日志可能是 1001。

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

console

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

日志消息模板

每個日志都會指定一個消息模板。 消息模板可包含要填寫參數(shù)的占位符。 請在占位符中使用名稱而不是數(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ù)。 在以下代碼中,請注意消息模板中的參數(shù)名稱未按順序排列:

C#

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

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

Parameter values: parm1, parm2

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

C#

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

如果要將日志發(fā)送到 Azure 表存儲,則每個 Azure 表實體都可具有 ID 和 RequestTime 屬性,這簡化了對日志數(shù)據(jù)的查詢。 無需分析文本消息的超時,查詢即可找到特定 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

日志篩選

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

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

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

項目模板代碼調(diào)用 CreateDefaultBuilder 來為控制臺和調(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ù)按提供程序和類別指定最低日志級別,如下方示例所示:

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 條用于控制臺提供程序, 1 條用于所有提供程序。 創(chuàng)建 ILogger 對象時,為每個提供程序選擇一個規(guī)則。

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

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

C#

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

第二個 AddFilter 使用類型名稱來指定調(diào)試提供程序。 第一個 AddFilter 應(yīng)用于全部提供程序,因為它未指定提供程序類型。

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

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

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

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

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

  • 選擇匹配提供程序或其別名的所有規(guī)則。 如果找不到任何匹配項,則選擇提供程序為空的所有規(guī)則。
  • 根據(jù)上一步的結(jié)果,選擇具有最長匹配類別前綴的規(guī)則。 如果找不到任何匹配項,則選擇未指定類別的所有規(guī)則。
  • 如果選擇了多條規(guī)則,則采用最后一條。
  • 如果未選擇任何規(guī)則,則使用 MinimumLevel。

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

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

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

提供程序別名

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

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

默認最低級別

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

C#

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

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

篩選器函數(shù)

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

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)類別和級別

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

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

日志作用域

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

范圍是由 BeginScope 方法返回的 IDisposable 類型,持續(xù)至釋放為止。 要使用作用域,請在 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);
}

下列代碼為控制臺提供程序啟用作用域:

Program.cs:

C#

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

 備注

要啟用基于作用域的日志記錄,必須先配置 IncludeScopes 控制臺記錄器選項。

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

每條日志消息都包含作用域內(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 的日志記錄選項。

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

控制臺提供程序

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

C#

logging.AddConsole();

要查看控制臺日志記錄輸出,請在項目文件夾中打開命令提示符并運行以下命令:

console

dotnet run

調(diào)試提供程序

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

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

C#

logging.AddDebug();

EventSource 提供程序

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

C#

logging.AddEventSourceLogger();

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

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

其他 Perfview 提供程序

Windows EventLog 提供程序

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

C#

logging.AddEventLog();

TraceSource 提供程序

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

C#

logging.AddTraceSource(sourceSwitchName);

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

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

Azure 中的日志記錄

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

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

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

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

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

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

C#

logging.AddAzureWebAppDiagnostics();

要配置提供程序設(shè)置,請使用 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)用時,應(yīng)用程序?qū)⒆裱?Azure 門戶中“應(yīng)用服務(wù)”頁面下的診斷日志部分的設(shè)置。 更新這些設(shè)置后,更改會立即生效,無需重新啟動或重新部署應(yīng)用。

Azure 日志記錄設(shè)置

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

該提供程序僅當項目在 Azure 環(huán)境中運行時有效。 項目在本地運行時,該提供程序無效 — 它不會寫入本地文件或 blob 的本地開發(fā)存儲。

Azure 日志流式處理

通過 Azure 日志流式處理,可從以下位置實時查看日志活動:

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

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

  • 從應(yīng)用的門戶頁導航到“診斷日志”頁。
  • 將“應(yīng)用程序日志記錄(Filesystem)”設(shè)置為“開”。

Azure 門戶診斷日志頁

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

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

Azure Application Insights 跟蹤日志記錄

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

第三方日志記錄提供程序

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

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

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

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

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號