1. 程式人生 > >.NET Core的日誌[2]:將日誌輸出到控制檯

.NET Core的日誌[2]:將日誌輸出到控制檯

對於一個控制檯應用,比如採用控制檯應用作為宿主的ASP.NET Core應用,我們可以將記錄的日誌直接輸出到控制檯上。針對控制檯的Logger是一個型別為ConsoleLogger的物件,ConsoleLogger對應的LoggerProvider型別為ConsoleLoggerProvider,這兩個型別都定義在 NuGet包“Microsoft.Extensions.Logging.Console”之中。 本文已經同步到《ASP.NET Core框架揭祕》之中]

目錄
一、ConsoleLogger
二、ConsoleLogScope
三、ConsoleLoggerProvider
四、擴充套件方法AddConsole

一、ConsoleLogger

如下所示的程式碼片段展示了ConsoleLogger型別的定義。它具有四個屬性,代表Logger名稱的Name屬性最初由ConsoleLoggerProvider提供,實際上就是LoggerFactory在建立Logger時指定的日誌型別(Category)。ConsoleLogger的Console屬性代表當前控制檯,它的型別為IConsole介面。之所以沒有直接採用System.Console向控制檯輸出格式化的日誌訊息,是因為需要提供跨平臺的支援,IConsole介面表示的就是這麼一個與具體平臺無關的抽象化的控制檯。

   1: public class
ConsoleLogger : ILogger
   2: {
   3:     public string                           Name { get; }
   4:     public IConsole                         Console { get; set; }
   5:     public Func<string, LogLevel, bool>     Filter { get; set; }
   6:     public bool                             IncludeScopes { get; set; }    
   7:  
   8:     public ConsoleLogger(string name, Func<string, LogLevel, bool> filter, bool includeScopes);
   9:     public IDisposable BeginScope<TState>(TState state);
  10:   
  11:     public bool IsEnabled(LogLevel logLevel);
  12:     public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);  
  13:     public virtual void WriteMessage(LogLevel logLevel, string logName, int eventId, string message);
  14: }

ConsoleLogger的Filter屬性通過一個委託物件來過濾真正需要寫到控制檯的日誌訊息,該屬性的返回型別為Func<string, LogLevel, bool>,兩個輸入引數分別表示分發給它的日誌訊息的型別和等級,如果執行該委託物件返回False,日誌訊息將會被直接忽略。ConsoleLogger的IsEnabled方法會直接將指定日誌等級作為引數(ConsoleLogger的Name屬性作為另一個引數)呼叫這個委託物件得到最終的返回結果。ConsoleLogger的IncludeScopes與上面介紹的日誌上下文範圍有關,我們會在後續的部分對它進行單獨介紹。

對於ConsoleLogger的這四個屬性,除了表示當前控制檯的Console屬性,其餘三個均可以在建立它的時候通過建構函式的相應引數來指定。接下來我們來簡單瞭解一下表示抽象化控制檯的IConsole介面,該介面具有如下三個方法。在呼叫Write和WriteLine方法向控制檯輸出內容的時候,我們除了指定寫入的訊息文字之外,還可以控制訊息在控制檯上的背景色和前景色。Flush方法與資料輸出緩衝機制有關,如果採用緩衝機制,通過Write或者WriteLine方法寫入的訊息並不會立即輸出到控制檯,而是先被儲存到緩衝區,Flush方法被執行的時候會將緩衝區的所有日誌訊息批量輸出到控制檯上。

   1: public interface IConsole
   2: {
   3:     void Write(string message, ConsoleColor? background, ConsoleColor? foreground);
   4:     void WriteLine(string message, ConsoleColor? background, ConsoleColor? foreground);    
   5:     void Flush();
   6: }

微軟預設提供了兩種型別的Console型別,一種是基於Windows平臺的WindowsLogConsole,非Windows平臺的控制檯則通過AnsiLogConsole來表示。它們之間的不同之處主要體現在設定控制檯上顯示顏色(前景色和背景色)的差異。對於Windows平臺來說,訊息顯示在控制檯顏色是通過顯式設定System.Console型別的靜態屬性ForegroundColor和BackgroundColor來實現的,但是對於非Windows平臺來說,顏色資訊會直接以基於ASNI標準的轉意字元序列(ANSI Esacpe Sequences)的形式內嵌在訊息文字之中)。

當ConsoleLogger的Log方法被呼叫的時候,它會先將指定的日誌等級作為引數呼叫IsEnabled方法。如果這個方法返回True,ConsoleLogger會呼叫WriteMessage方法將提供的日誌訊息輸出到由Console屬性表示的控制檯上。WriteMessage方法是一個虛方法,如果它輸出的訊息格式和樣式不滿足我們的要求,我們可以定義ConsoleLogger的子類,並通過重寫這個方法按照我們希望的方式輸出日誌訊息。

   1: {LogLevel} : {Category}[{EventId}]
   2: {Message}

在預設情況下,被ConsoleLogger輸出到控制檯上的日誌訊息會採用上面的格式,這也可以通過我們在上面演示的例項來印證。對於輸出到控制檯表示日誌等級的部分,輸出的文字與對應的日誌等級具有如下表所示的對映關係,可以看出日誌等級在控制檯上均會顯示為僅包含四個字母的簡寫形式。日誌等級也同時決定了改部分內容在控制檯上顯示的前景色。

日誌等級

顯示文字

前景顏色

背景顏色

Trace

trce

Gray

Black

Debug

dbug

Gray

Black

Information

info

DarkGreen

Black

Warning

warn

Yellow

Black

Error

fail

Red

Black

Critical

crit

White

Red

二、ConsoleLogScope

在預設情況下針對Log方法的每次呼叫都是一次獨立的日誌記錄行為,對於輸出到控制檯的多個日誌訊息,我們也看不出它們是否具有某種關聯。在很多情況下多次相關的日誌記錄需要在同一個日誌上下文範圍中進行,那麼輸出到控制檯上的多條日誌訊息將具有相同的上下文資訊而被關聯起來,我們可以通過呼叫Logger的BeginScope方法來建立這個日誌上下文範圍。ConsoleLogger的BeginScope方法建立的日誌上下文範圍與一個具有如下定義的ConsoleLogScope類有關。

   1: public class ConsoleLogScope
   2: {   
   3:     internal ConsoleLogScope(string name, object state);
   4:     public static IDisposable Push(string name, object state);
   5:     public override string ToString();
   6:  
   7:     public static ConsoleLogScope     Current { get; set; }
   8:     public ConsoleLogScope            Parent { get; set; }
   9: }

我們說ConsoleLogger的BeginScope方法返回的日誌上下文範圍與ConsoleLogScope有關,但並沒有說該方法返回的是一個ConsoleLogScope物件。這一點從上面給出的ConsoleLogScope型別定義也可以看出來,BeginScope方法返回型別為IDisposable介面,但是ConsoleLogScope並未實現該介面。如上面的程式碼片段所示,ConsoleLogScope只定義了一個內部建構函式,所以我們不可以直接呼叫建構函式建立一個ConsoleLogScope物件,ConsoleLogScope的建立實現在它的靜態方法Push中,ConsoleLogger的BeginScope方法的返回值其實就是呼叫這方法的返回值。

要理解Push方法中針對ConsoleLogScope的建立邏輯,我們需要先來了解一下ConsoleLogScope的巢狀層次結構。一個ConsoleLogScope可以內嵌於另一個ConsoleLogScope之中,後者被稱為前者的“父親”,它的Parent屬性返回的就是這麼一個物件。ConsoleLogScope的靜態屬性Current表示當前的ConsoleLogScope,當我們通過指定name和state這兩個引數呼叫靜態方法Push時,該方法實際上會呼叫靜態建構函式建立一個新的ConsoleLogScope物件並將其作為當前ConsoleLogScope的“兒子”。於此同時,當前ConsoleLogScope被切換成這個新建立的ConsoleLogScope。

ConsoleLogScope的Push方法最終返回的是一個DisposableScope物件。如下面的程式碼片段所示,DisposableScope僅僅是內嵌於ConsoleLogScope的一個私有型別。當它的Dispose方法執行的時候,它僅僅是獲取當前ConsoleLogScope的“父親”,並將後者作為當前ConsoleLogScope。

   1: public class ConsoleLogScope
   2: {
   3:     public static IDisposable Push(string name, object state)
   4:     {
   5:         ConsoleLogScope current = Current;
   6:         Current = new ConsoleLogScope(name, state);
   7:         Current.Parent = current;
   8:         return new DisposableScope();
   9:     }
  10:  
  11:     private class DisposableScope : IDisposable
  12:     {
  13:         public void Dispose()
  14:         {
  15:             ConsoleLogScope.Current = ConsoleLogScope.Current.Parent;
  16:         }
  17:     }
  18: }

簡單地說,我們呼叫ConsoleLogScope的靜態Push方法會建立當前日誌上下文範圍並返回一個DisposableScope物件,一旦我們呼叫這個DisposableScope物件的Dispose方法,這就意味著這個上下文範圍的終結。與此同時,原來的ConsoleLogScope從新成為當前的日誌上下文。下面的程式碼片段體現了ConsoleLogScope針對作用域控制方式,這段程式碼來體現另一個細節,那就是ConsoleLogScope型別的ToString方法被重寫,它返回的是ConsoleLogScope物件被建立時指定的State物件(state引數)的字串形式(呼叫ToString方法的返回值)。

   1: using (ConsoleLogScope.Push("App", "Scope1"))
   2: {
   3:     Debug.Assert("Scope1" == ConsoleLogScope.Current.ToString());
   4:     using (ConsoleLogScope.Push("App", "Scope1"))
   5:     {
   6:         Debug.Assert("Scope2" == ConsoleLogScope.Current.ToString());
   7:     }
   8:     Debug.Assert("Scope1" == ConsoleLogScope.Current.ToString());
   9: }

再次將我們目光從ConsoleLogScope轉移到ConsoleLogger上面,當ConsoleLogger的BeginScope方法被呼叫的時候,它會將自己的名稱(Name屬性)和指定的State物件作為引數呼叫ConsoleLogScope的靜態方法Push並返回一個DisposableScope物件。只要我們沒有呼叫DisposableScope的Dispose方法,就可以通過呼叫ConsoleLogScope的靜態屬性Current得到當前日誌上下文,它的ToString方法和指定的State物件的ToString方法返回相同的字串。

   1: public class ConsoleLogger : ILogger
   2: {
   3:    public IDisposable BeginScope<TState>(TState state)
   4:    {  
   5:       return ConsoleLogScope.Push(this.Name, state);
   6:    }
   7: }

預設情況下,ConsoleLogger針對日誌上下文範圍的支援是關閉的,我們需要利用它的IncludeScopes屬性開啟這個特性。如果ConsoleLogger的Log方法是在某個日誌上下文範圍中被呼叫,它會採用如下的格式輸出日誌訊息,其中{State}表示呼叫BeginScope方法傳入的State物件。

   1: {LogLevel} : {Category}[{EventId}]
   2:            =>{State}
   3:           {Message}

比如在一個處理訂購訂單的應用場景中,如果需要將針對同一筆訂單的多條日誌訊息關聯在一起,我們就可以針對訂單的ID建立一個日誌上下文範圍,並在此上下文範圍內呼叫Logger物件的Log方法進行日誌記錄,那麼訂單ID將會包含在每條寫入的日誌訊息中。

   1: ILogger logger = new ServiceCollection()
   2:     .AddLogging()
   3:     .BuildServiceProvider()
   4:     .GetService<ILoggerFactory>()
   5:     .AddConsole(true)
   6:     .CreateLogger("Ordering");
   7:  
   8: using (logger.BeginScope("訂單: {ID}", "20160520001"))
   9: {
  10:     logger.LogWarning("商品庫存不足(商品ID: {0}, 當前庫存:{1}, 訂購數量:{2})", "9787121237812",20, 50);
  11:     logger.LogError("商品ID錄入錯誤(商品ID: {0})","9787121235368");
  12: }

如上面的程式碼片段所示,我們按照依賴注入的程式設計方式建立了一個註冊有ConsoleLoggerProvider的LoggerFactory,並利用建立了一個Logger物件。我們在呼叫擴充套件方法AddConsole方法註冊ConsoleLoggerProvider 的時候傳入True作為引數,意味著提供的ConsoleLogger會在當前的日誌上下文範圍中進行日誌記錄(它 的IncludeScope屬性被設定為True)。我們通過Logger物件記錄了兩條針對同一筆訂單的日誌,兩次日誌記錄所在的上下文範圍是呼叫BeginScope方法根據指定 的訂單ID建立的。這段程式執行之後會在控制檯上輸出如下所示的兩條日誌訊息。

   1: warn: Ordering[0]
   2:       => Order ID: 20160520001
   3:       商品庫存不足(商品ID: 9787121237812, 當前庫存:20, 訂購數量:50)
   4: fail: Ordering[0]
   5:       => Order ID: 20160520001
   6:       商品ID錄入錯誤(商品ID: 9787121235368)

三、ConsoleLoggerProvider

ConsoleLogger最終通過註冊到LoggerFactory上的ConsoleLoggerProvider來提供。當我們在建立一個ConsoleLogger的時候,除了需要指定它的名稱之外,還需要指定一個用於過濾日誌的Func<string, LogLevel, bool>物件,以及用於確定是否將日誌寫入操作納入當前上下文範圍的布林值。這兩者最終都需要通過ConsoleLoggerProvider來提供,我們在呼叫建構函式建立ConsoleLoggerProvider的時候需要將它們作為輸入引數。

   1: public class ConsoleLoggerProvider : ILoggerProvider, IDisposable
   2: {    
   3:     public ConsoleLoggerProvider(Func<string, LogLevel, bool> filter,bool includeScopes);
   4:     public ConsoleLoggerProvider(IConsoleLoggerSettings settings);
   5:  
   6:     public ILogger CreateLogger(string name);
   7:     public void Dispose();
            
           

相關推薦

.NET Core日誌[2]:日誌輸出控制檯

對於一個控制檯應用,比如採用控制檯應用作為宿主的ASP.NET Core應用,我們可以將記錄的日誌直接輸出到控制檯上。針對控制檯的Logger是一個型別為ConsoleLogger的物件,ConsoleLogger對應的LoggerProvider型別為ConsoleLoggerProvider,這兩個型別都

asp.net core使用serilog日誌推送到騰訊雲日誌服務

為什麼是serilog? Serilog是 .NET 中最著名的結構化日誌類庫。 基於日誌事件log events,而不是日誌訊息log message。 你可以將日誌事件格式化為控制檯的可讀文字或者可以將相同的事件格式化為JSON並將其傳送到遠端日誌伺服器。 應用程式中的日誌語句會建立LogEvent物件,

.NET Core日誌[4]:日誌寫入EventLog

面向Windows的程式設計人員應該不會對Event Log感到陌生,以至於很多人提到日誌,首先想到的就是EventLog。EventLog不僅僅記錄了Windows系統自身針對各種事件的日誌,我們的應用也可以利用提供的API將日誌訊息寫到EventLog中。與EventLog相關的API都定義在System

.NET Core日誌[3]:日誌寫入Debug視窗

定義在NuGet包“Microsoft.Extensions.Logging.Debug”中的DebugLogger會直接呼叫Debug的WriteLine方法來寫入分發給它的日誌訊息。如果需要使用DebugLogger來寫日誌,我們需要將它的提供者DebugLoggerProvider註冊到LoggerFa

總結:利用asp.net core日誌進行生產環境下的錯誤排查(asp.net core version 2.2,用IIS做伺服器)

概述 除錯asp.net core程式時,在輸出視窗中,在輸出來源選擇“除錯”或“xxx-ASP.NET Core Web伺服器”時,可以看到類似“info:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 28

.NET Core使用log4Net記錄日誌

com hit appendto append and logger pub his manage 1.引入Nuget包 log4net 2.添加log4Net配置文件 1 <?xml version="1.0" encoding="utf-8" ?> 2

.NET Core使用Nlog記錄日誌

microsoft except urn odin conf -c lena 註入 eight 1.引入Nuget包 Nlog Nlog.Web.AspNetCore 2.添加nlog配置文件 1 <?xml version="1.0"

ASP.NET Core 實戰: .NET Core 2.0 專案升級到 .NET Core 2.1

 一、前言    最近一兩個星期,加班,然後回去後弄自己的部落格,把自己的電腦從 Windows 10 改到 Ubuntu 18.10 又弄回 Windows 10,原本計劃的學習 Vue 中生命週期的相關知識目前也沒有任何的進展,嗯,罪過罪過。看了眼時間,11月也快要結束了,準備補上一

用 Log4Net 三步實現 .Net Core 類庫 分日誌等級(不同檔案目錄)存日誌

1,建立.Net Core  Web 專案,引入 log4net 2,建立類庫,新增如下 helper方法 1 using log4net; 2 using log4net.Config; 3 using System; 4 using Syste

Asp.Net Core 輕鬆學-利用日誌監視進行服務遙測

前言     在 Net Core 2.2 中,官方文件表示,對 EventListener 這個日誌監視類的內容進行了擴充,同時賦予了跟蹤 CoreCLR 事件的許可權;通過跟蹤 CoreCLR 事件,比如通過跟蹤 CoreCLR 事件,可以瞭解和收集到比如 GC,JIT,ThreadPool,intre

.net core 微服務之日誌落盤設計

oss val www. strong 導致 在線 ron 計算器 name 原文:.net core 微服務之日誌落盤設計 目錄1、設計目標2、日誌流程3、串聯請求事務3.1

log4j2配置按照日誌級別日誌輸出到不同的文件

hold eve open ror .com 可能 資料 clas opener 背景 在項目中,可能會產生非常多的日誌記錄,為了方便日誌分析,可以將日誌按級別輸出到指定文件。 log4j2.xml配置文件 <!--將info級別的日誌

Python - loguru日誌庫,高效輸出控制檯日誌日誌記錄

安裝loguru loguru的PyPI地址為:https://pypi.org/project/loguru/ GitHub倉庫地址為:https://github.com/Delgan/loguru 我們可以直接使用pip命令對其進行安裝 pip install loguru  或者下載其原始碼

.Net Core 整合ExceptionLess分散式日誌框架之本地化部署

## 前言 公司目前使用的專案中關於日誌記錄這塊,之前一直都是使用的Log4net 存放於後臺文件中的,對於異常錯誤啊,或者需要檢視一些詳情錯誤的時候感覺很不方便,要到伺服器上去開啟日誌檔案檢索錯誤,降低了解決問題的速度,所以針對日誌這塊的優化就提上日程了。考慮了好久決定採用ExceptionLess日誌框

.Net Core中的診斷日誌DiagnosticSource講解

### 前言     近期由於需要進行分散式鏈路跟蹤系統的技術選型,所以一直在研究鏈路跟蹤相關的框架。作為能在.Net Core中使用的APM,SkyWalking自然成為了首選。[SkyAPM-dotnet](https://github.com/SkyAPM/Sky

一套標準的ASP.NET Core容器化應用日誌收集分析方案

## 講故事 關注我公眾號的朋友,應該知道我寫了一些雲原生應用收集和分析相關的文章,其中內容大多聚焦某個具體的元件: - 超級有用的TraceId,快點用起來吧! - 如何利用NLog輸出結構化日誌,並在Kibana優雅分析日誌? | - 既然能直接向ElasticSearch寫日誌,為什麼

ASP.NET Core擴充套件庫之日誌

    上一篇我們對Xfrogcn.AspNetCore.Extensions擴充套件庫功能進行了簡單的介紹,從這一篇文章開始,我將逐步介紹擴充套件庫中的核心功能。     日誌作為非業務的通用領域基礎功能,有非常多的技術實現,這些第三方庫避免了我們花費時間去重複實現,

Linux環境安裝.Net Core SDK 2.0

yum 2.0 img mic oca tar.gz .cn log .tar.gz 1.安裝libicu依賴 yum install libunwind libicu 2.下載SDK2.0 wget https://download.microsoft.com/d

【視頻】ASP.NET Core MVC 2.* 入門

配置 page bili http www 三方庫 play help uget 比較初級的入門教程,網址在B站:https://www.bilibili.com/video/av33728783/ 內容如下: 1. ASP.NET Core 簡介和開發工具 2. A

用於Visual Studio的ASP.NET Core + Angular 2模板

用於Visual Studio的ASP.NET Core + Angular 2模板 既然ASP.NET Core,Angular 2和TypeScript 2都已經發布了最終版本,那麼現在是將它們全部組合到一個功能強大的富Web應用程式平臺中的好時機。 釋出於2016年10月4日 很多