ASP.NET Core 1.0基礎之日志


過年出去玩了一圈,回來繼續翻譯。前兩天偷懶沒有翻譯,只是轉了兩篇C# 7計划中的新features,大家還是很支持的。現在繼續完善這個系列。

來源https://docs.asp.net/en/latest/fundamentals/logging.html

ASP.NET Core 1.0提供了內置的日志模塊,當然開發者也能使用自己偏愛的日志框架。使用日志模塊的化,需要現在應用中添加很少的一點setup 代碼。做完這些,你就可以在應用中隨處可以添加日志啦。

在應用中實現日志模塊##

通過依賴注入請求ILoggerFactory或者ILogger 可以添加日志到應用中的模塊中。如果請求的是ILoggerFactory那么logger必須使用它的CreateLogger方法來創建。如下展示了如何在在Startup類中的Configure方法中進行設置。

public void Configure(IApplicationBuilder app,
    IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(minLevel:LogLevel.Verbose);

    app.UseStaticFiles();

    app.UseMvc();

    // Create a catch-all response
    app.Run(async (context) =>
    {
        var logger = loggerFactory.CreateLogger("Catchall Endpoint");
        logger.LogInformation("No endpoint found for request {path}", context.Request.Path);
        await context.Response.WriteAsync("No endpoint found - try /api/todo.");
    });

logger創建時必須提供category名字。該名字指定了日志事件的來源。按照約定名字是分層級的,category是由dot(.)來分開的。利用這項約定,一些日志provider有過濾功能,這樣就可以更方便的定位日志。如上例子,logging配置成使用內置的ConsoleLogger。你可以運行命令行web命令,請求配置好的url(localhost:5000),就可以ConsoleLogger在運行的。輸出類似下圖。
consolelogger

如上,當每次使用瀏覽器請求時,會在Console中看到多條日志記錄,因為大部分瀏覽器會在加載網頁時發出多次請求的。注意到Console logger顯示的info類型的日志,並且日志信息是顯示在category [Catchall Endpoint]之后的。
log方法調用可以使用帶有命名占位符的格式化字符串,這些占位符可以按順序填充調用時傳給方法的參數。一些logging provider會將參數名和對應的值作為字典存儲起來以備稍后使用。如下例子,請求路徑作為命名占位符傳進來。

var logger = loggerFactory.CreateLogger("Catchall Endpoint");
logger.LogInformation("No endpoint found for request {path}", context.Request.Path);

在實際應用中,你可能想添加應用層級的日志,而不是框架層級的日志。例如,你創建了Web Api項目來管理To-Do項目,你可能需要對於針對todo項目的各種操作來記錄日志。
假設API的邏輯是在TodoController內,它通過構造器注入來請求服務。理想情況下,類應該像該例子中的一樣,使用構造器來顯式定義他們的依賴參數。而不是顯式的請求ILoggerFactory並創建ILogger實例。TodoController顯示了另一種在應用中使用logger的方式,你可以請求ILogger (這里T是請求logger的類)。

[Route("api/[controller]")]
public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger<TodoController> _logger;

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

    [HttpGet]
    public IEnumerable<TodoItem> GetAll()
    {
        _logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all items");
        EnsureItems();
        return _todoRepository.GetAll();
    }

在controller內的每個action內,是通過類內的私有字段_logger來寫日志的。這種方法不僅可以用在Congroller內,也可以通過依賴注入在應用中的其他服務中使用。

使用ILogger ##

如上所示,在應用中,可以通過類構造器來請求ILogger ,其中T是執行日志記錄的類。TodoController中就是該方法的一個例子。使用這項技術時,logger是自動使用類型的名字作為category的名字。通過請求ILogger 實例,類就不需要使用ILoggerFactory來創建logger實例。你可以在任何地方使用這種方式,只要你不需要ILoggerFactory提供的其他額外的功能。

日志級別##

在應用中添加日志時,必須指定LogLevel。LogLevel允許你控制應用的日志輸出,以及將不同類型的日志輸入到不同的logger。例如,你可能希望將debug日志輸出到本地文件,但是輸出錯誤到machine event log或者數據庫。

ASP.NET Core 1.0定義了6中不同級別。
Debuge
適用於最詳細的日志信息,尤其對開發者在進行debug時特別有用。這些信息可能包含應用敏感信息,因此在生產環境是不應該使用的。默認是Disabled的。敏感信息如Credentials: {"User":"someuser", "Password":"P@ssword"}
Verbose
這種類型的信息是在開發中臨時有用的。可能包含了對debug有用的信息,但是沒有長期價值。這是大部分日志默認的級別。例如Entering method Configure with flag set to true。
Infomation
這種信息用來追蹤應用的一般流程(General flow)。這些日志應該具有長期價值。例如Request received for path /foo
Warning
Warning級別用於記錄應用程序流程中反常的未預期的事件。它可能包含了一些錯誤,也可能是一些並沒有引起程序down的但是需要在未來進行處理的情況。處理異常通常使用Warning級別的日志。例如Login failed for IP 127.0.0.1 or FileNotFoundException for file foo.txt。
Error
由於某些錯誤,應用程序的當前流程必須停止時,需要記錄Error級別的日志,比如未處理或不能恢復的異常。這些信息標識當前活動或操作(比如當前http請求)出現失敗,而不是application-wide層級的錯誤。
Critical
Critical日志用來記錄uncoverable應用和系統錯誤,或者需要立馬處理的災難性的錯誤。例如data loss scenarios, stack overflows, out of disk space。

Logging包對這些標准的日志級別提供了一些helper擴展方法,允許你調用LogInformation而不是更verbose的Log方法(如LogLeve.Infomation)。每種LogeLevel特定的擴展方法有幾個重載,允許你傳遞如下參數的一些或者全部。

string data
log信息。
int eventId
日志相關的數字Id,用來關聯日志事件。事件Id應該是static的,針對特定的類型的日志事件。例如,你可以把事件id 1000關聯到添加物品到購物車的事件,而完成購買的事件則是關聯到1001的Id。這允許智能過濾和process of log statement。
string format
格式化的log信息。
object[] args
需要格式化的對象數組。
Exception Error
需要記錄的異常實例。

Note: 一些logger,比如內置的ConsoleLogger,會忽略eventId。如果你需要顯示它,你需要將它包含在message字符串中。接下來的例子中,就是這種做法,你可以輕易的看到eventId關聯到每個信息,但是實際中你可能不會將其包含在log message中。

在TodoController的例子中,對每個事件定義一個事件id常量,log statements are configured at the appropriate verbosity level based on the success of the operation。在本例中,成功操作的日志級別為Information,未找到的操作日記記錄為Warning級別(未顯示錯誤處理)。

[HttpGet]
    public IEnumerable<TodoItem> GetAll()
    {
        _logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all items");
        EnsureItems();
        return _todoRepository.GetAll();
    }

    [HttpGet("{id}", Name = "GetTodo")]
    public IActionResult GetById(string id)
    {
        _logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {0}", id);
        var item = _todoRepository.Find(id);
        if (item == null)
        {
            _logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({0}) NOT FOUND", id);
            return HttpNotFound();
        }
        return new ObjectResult(item);
    }

Note: 推薦在應用級別和它的API級別記錄日志,而不是框架級別。框架已經有了內置的日志,可以通過合適的日志verbosity level來開啟。

為了看更詳細的框架級別的日志,你可以調整你的logging provider的LogLevel為更verbose的層級(如Debug或者Verbose)。例如,如果在Configure方法修改AddConsole調用為LogLevel.Verbose,然后運行程序,會顯示更多框架級別的請求相關的詳細信息。
detail

The console logger prefixes verbose output with “verbose: ” and uses a gray font to make it easier to distinguish it from other levels of log output.

Scopes##

在應用程序中記錄日志時,你可以把一個scope內的一系列邏輯相關的操作分為一個group。scope是由調用BeginScopeImpl方法返回的IDisposable類型,它從創建持續到Dispose。內置的 TraceSource logger 就返回一個scope實例,它負責開始和結束tracing操作。任何日志狀態,如trnsaction id,都在scope創建時附加進來。

scope並不是必須的,如果使用的話,應該節儉的使用。它應該針對那些有明顯開始和結束的操作來使用,如涉及多個資源的事務。

在應用中配置日志##

在ASP.NET應用中配置日志,你應該在Startup類中的Configure方法中解析ILoggerFacotry。當添加該類型的參數到Configure方法中,ASP.NET會自動通過依賴注入提供一個ILoggerFactory實例。一旦你添加了ILoggerFactory 作為參數,你可以在Configure方法中,通過調用loggerfactory的擴展方法來配置logger。在本文初,我們已經看到這樣的一個例子,其中我們通過調用loggerFacotry.AddConsole添加了一個Console logging。除了添加logger,我們還可以通過loggerfacotry的MinimumLevel屬性來控制應用中日志的verbosity級別。默認的verbosity是Verbose。
Note:對於每個特定的logger provider你也可以指定日志minimum級別。例如,AddConsole擴展方法支持可選參數來設置它的minimum日志級別。

配置TraceSource日志###

內置的 TraceSourceLogger provier提供一種簡單的方式利用當前的System.Diagnostics.TraceSource庫和provider來配置記錄日志信息,包括接入windows event log。這種已證明的將信息路由到不同的listeners,已經被廣泛使用。TraceSourceLogger 可以讓開發者來使用這種方法。

首先,確保在項目的Project.json中添加了Microsoft.Extensions.Logging.TraceSource包。

"dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.TraceSource": "1.0.0-rc1-final"
  },

如下例子顯示了如何在應用中配置兩個相互獨立的TraceSourceLogger實例,兩個都是記錄critical級別的信息。每次調用AddTraceSource 方法都接收一個TraceListener。第一個調用配置了ConsoleTraceListener。第二個配置了EventLogTraceListener,來寫應用事件日志。這兩個listener在DNX Core中都是不可用的,因此這些配置是包含的條件編譯中的。

            loggerFactory.MinimumLevel = LogLevel.Debug;
#if DNX451
            var sourceSwitch = new SourceSwitch("LoggingSample");
            sourceSwitch.Level = SourceLevels.Critical;
            loggerFactory.AddTraceSource(sourceSwitch,
                new ConsoleTraceListener(false));
            loggerFactory.AddTraceSource(sourceSwitch,
                new EventLogTraceListener("Application"));
#endif

如上例子也展示了設置logger factory的MinimumLevel。然而,這只是對新的facotry的簡單默認設置,仍然可以被單獨的配置logger來重寫的。在本例中,sourceSwitch配置為SourceLevels.Critical,所以只有critical的信息才會被兩個listener所收集。

為了測試代碼,用如下代碼替代catch-all響應。

app.Run(async context =>
{
        if (context.Request.Path.Value.Contains("boom"))
        {
                throw new Exception("boom!");
        }
        await context.Response.WriteAsync("Hello World!");
});

之后,當程序在windows上運行時,請求路徑為http://localhost:5000/boom,控制台將輸出如下。
tracing

在Windows Event Viewer中查看應用事件日志,如下事件會在本次請求后被記錄。
eventViewer

除了使用TraceSourceLogger,你也可以利用 EventLog logging provider直接將日志記錄到event log中。使用Debug logging provider可以支持使用System.Diagnostics.Debug.WriteLine來記錄日志,其輸出可以在vs的OutPut window中看到。

配置其他的provider###

除了內置的logger,你也可以使用別的provider配置logging。添加合適的包到project.json中,然后像其他provider一樣進行配置。一般情況下,這些包應該包括ILoggerFactory的擴展方法,以便於方便添加他們。
Note:目前ASP.NET團隊在和第三方logging provider合作,來支持這種logging model。 Once these ship, we will include links to them here。

當然你也可以創建自己的provider,來支持別的logging框架或者自己內部的需求。

日志記錄推薦做法##

如下是一些推薦做法,當在ASP.NET應用中實施日志時會帶來幫助。

  1. 使用正確的LogLevel來記錄日志。這樣可以基於日志的重要程度來合適的消費和路由日志。
  2. 避免記錄冗余或無關的日志,而是記錄可以快速定位錯誤的日志。
  3. 保持日志精簡而不犧牲重要的信息。
  4. 盡管在disabled的情況下logger不會工作,consider adding code guards around logging methods to prevent extra method calls and log message setup overhead, especially within loops and performance critical methods。
  5. 使用唯一的前綴來命名logger,這樣他們能被方便的filtered和disabled。記住Create 會創建已類型的全名來命名的logger。
  6. 保守的使用scope,僅僅針對有明確開始和結束的操作來使用。例如,框架針對MVC action提供了scope。避免在scope中嵌套較多的scope。
  7. 應用日志代碼應該與應用程序的業務關注點相關管。提高logging verbosity來顯示額外的框架級別的關注點,而不是實現自己的。

總結##

ASP.NET提供了內置的對日志的支持,可以在Startup類中方便配置,然后在整個應用中使用。Logging verbosity can be configured globally and per logging provider to ensure actionable information is logged appropriately. Built-in providers for console and trace source logging are included in the framework; other logging frameworks can easily be configured as well.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM