.NET Core的日誌[3]:將日誌寫入Debug視窗
定義在NuGet包“Microsoft.Extensions.Logging.Debug”中的DebugLogger會直接呼叫Debug的WriteLine方法來寫入分發給它的日誌訊息。如果需要使用DebugLogger來寫日誌,我們需要將它的提供者DebugLoggerProvider註冊到LoggerFactory上。由於定義在Debug型別中的所有方法都是針對Debug編譯模式的,所以在只有針對Debug模式編譯的應用中使用DebugLogger才有意義。這裡將的“Debug編譯模式”涉及到一個叫做“條件編譯”的話題。 本文已經同步到《ASP.NET Core框架揭祕》之中]
目錄
一、Debug型別與條件編譯
二、DebugLogger
三、DebugLoggerProvider
一、Debug型別與條件編譯
DebugLogger適用於.NET Framework和.NET Core應用,我們說DebugLogger最終是通過呼叫Debug型別的靜態方法WriteLine來寫入分發給它的日誌訊息,但是使用的這個Debug型別在.NET Framework和.NET Core應用下其實是兩個完全不同的型別。針對.NET Framework的Debug型別定義在程式集“System.dll”下,而針對.NET Core的Debug型別則承載於“System.Diagnostics.Debug”這個NuGet包中,這兩個Debug方法具有不同的API定義。
這兩個Debug型別針對日誌的寫入機制也不盡相同,針對.NET Framework的Debug型別定會利用註冊到Debug.Listeners屬性TraceListener來寫日誌,預設註冊的DefaultTraceListener會通過呼叫Win32函式OutputDebugString將格式化的日誌訊息輸出給Debug監視器(Debug Monitor)。對於針對針對.NET Core的Debug型別來說,它針對不同的平臺具有不同的實現,針對Windows平臺下日誌訊息依然是通過呼叫OutputDebugString
雖然兩個Debug型別在API定義和寫入日誌的實現都不同,但是對於被DebugLogger用來寫日誌的WriteLine方法來說,它們都具有如下所示的定義方式。該方法具有兩個引數,分別代表寫入日誌的文字訊息和型別。我們可以看到這個方法上標註了一個型別為ConditionalAttribute的特性,它具有一個值為“DEBUG”的引數。這個ConditionalAttribute特性就與我們所說的“條件編譯”有關。
1: public static class Debug
2: {
3: [Conditional("DEBUG")]
4:public static void WriteLine(string message,string category);
5: }
所謂的“條件編譯”,就是說編譯器在進行編譯的時候會根據指定的條件來過濾參與編譯的原始碼,這個原始碼過濾條件是在編譯時指定的符號化的字串,我們稱它為“條件編譯符(Conditional Compilation Symbol)”,上面程式碼片段中作為ConditionalAttribute特性引數的“DEBUG”就是條件編譯符。如果我們使用Visual Studio作為IDE,我們可以利用它以視覺化的方式來為某個的專案設定一個或者多個就是條件編譯符。我們只需要右擊某個專案並在彈出的上下文選單中選擇“屬性(Properties)”,然後按照如下圖所示方式在顯示的專案屬性視窗中選擇“生成(Build)”選項卡。
如圖8所示,我們可以定義任意字串作為條件編譯符(比如“UAT”,“SIT”)。除此之外,Visual Studio還為我們預設了“DEBUG”和“TRACE”這兩個常用的條件編譯符,如果需要我們只需要選擇相應的複選框(“Define DEBUG/TRACE constant”)即可。我們通過這種方法設定的條件編譯符最終會作為編譯選項以如下的方式寫入到project.json檔案中,具體的配置專案為“buildOptions/define”,換句話說,我們完全可以直接編輯project.json檔案的方式來定義條件編譯符。
1: {
2: ...
3: "buildOptions": {
4: "define": [ "DEBUG", "TRACE", "UAT, SIT" ]
5: }
6: }
從某種意義來說,條件編譯符實際上是為應用定義相應的“部署場景”,比如我們在上邊定義的條件編譯符“UAT”和“SIT”就是針對兩種不同型別(使用者接收測試和系統整合測試)的測試部署場景。如果我們需要編寫針對具有某種部署場景的程式,可以採用預編譯指令“#if/#endif”來實現。如果編譯器在編譯如下一段程式碼的時候,只有指定的條件編譯符包含“DEBUG”的情況下,呼叫WriteDebug方法的這段程式碼才會參與編譯,否則這段程式碼將直接被忽略。
1: #if DEBUG
2: WriteDebug(message);
3: #endif
完全採用預編譯指令“#if/#endif”來編寫針對具體某個條件編譯符的程式碼其實是很繁瑣的。如果某個方法總是針對具體某個條件編譯符,我們可以直接在這樣的方法上標註一個ConditionalAttribute特性,並將對應的條件編譯符作為其引數即可。比如上面這個WriteDebug方法就可以採用如下的方式來定義,它可以作為一個普通的方法來呼叫,而無需再使用任何預編譯指令。
1: [Conditional(“DEBUG”)]
2: public static void WriteBug(string message);
編譯器在編譯我們的程式的時候,如果程式中呼叫了某個標註了ConditionalAttribute特性的方法並且指定的條件編譯符與當前不一致,針對該方法呼叫的程式碼將被自動忽略。定義在Debug型別上的WriteLine方法上就標註了這麼一個ConditionalAttribute特性,指定的編譯符為“DEBUG”,大家應該知道為什麼DebugLogger為什麼只有針對Debug模式編譯生成的應用才後意義了吧。
二、DebugLogger
在瞭解了Debug型別和條件編譯的背景知識後,我們來正式認識一下DebugLogger型別。我們採用如下一段現對簡介的程式碼模擬了DebugLogger的定義。當我們呼叫建構函式建立一個DebugLogger物件的時候需要指定Logger的名稱和進行日誌過濾的Func<string, LogLevel, bool>物件,後者是可選的。DebugLogger呼叫Debug的WriteLine方法來進行日誌寫入體現在它的Log方法中,寫入的日誌訊息將DebugLogger的名稱作為日誌型別。
1: public class DebugLogger : ILogger
2: {
3: private readonly Func<string, LogLevel, bool> _filter;
4: private readonly string _name;
5:
6: public DebugLogger(string name, Func<string, LogLevel, bool> filter)
7: {
8: _name = string.IsNullOrEmpty(name) ? "DebugLogger" : name;
9: _filter = filter?? ((cate, level) => true);
10: }
11:
12: public DebugLogger(string name) : this(name, null)
13: {}
14:
15:
16: public IDisposable BeginScope<TState>(TState state)
17: {
18: return NoopDisposable.Instance;
19: }
20:
21: public bool IsEnabled(LogLevel logLevel)
22: {
23: return Debugger.IsAttached && _filter(_name, logLevel);
24: }
25:
26: public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
27: {
28: if (this.IsEnabled(logLevel))
29: {
30: string message = formatter(state, exception);
31: message = $"{logLevel}: {message}";
32: if (exception != null)
33: {
34: message = $"{message}{Environment.NewLine}{Environment.NewLine}{exception}";
35: }
36: Debug.WriteLine(message, _name);
37: }
38: }
39:
40: private class NoopDisposable : IDisposable
41: {
42: public static DebugLogger.NoopDisposable Instance = new DebugLogger.NoopDisposable();
43: public void Dispose()
44: {}
45: }
46: }
上面這段程式碼和體現了DebugLogger進行日誌記錄的一些細節特性:
- 如果呼叫建構函式指定的名稱為Null或者是一個空字串,建立的DebugLogger物件將使用它的型別名(“DebugLogger”)來命名。如果作為日誌過濾器的Func<string, LogLevel, bool>物件沒有顯式指定,意味著不需要對日誌進行過濾。
- DebugLogger並不支援日誌上下文,所以它的BeginScope<TState>方法返回的NoopDisposable物件並承載任何上下文資訊,這也是DebugLogger的建構函式不像ConsoleLogger一樣具有一個includeScope引數的原因。
- DebugLogger的IsEanbled方法不僅僅利用構造時指定的作為日誌過濾器的Func<string, LogLevel, bool>物件來決定是否真正寫入日誌,還需要考慮偵錯程式是否附加到當前程序(Debugger.IsAttached),只有這個兩個條件都滿足的情況下,這個方法才會返回True。
- DebugLogger的Log方法在真正寫入日誌的過程中,它會利用指定的作為格式化器的Func<TState, Exception, string>物件將承載原始日誌資訊的物件和異常(對應引數state和exception)格式成一個完整的字串作為最終寫入的日誌訊息。但是在指定的Exception物件不為Null的情況下,它又會在這個格式好的日誌訊息上附加上異常資訊,這其實是不太合理的。
三、DebugLoggerProvider
DebugLogger對應的LoggerProvider是一個DebugLoggerProvider物件。如下面的程式碼片段所示,DebugLoggerProvider提供DebugLogger的邏輯非常簡單,它只需要在實現的CreateLogger方法中呼叫建構函式建立並返回一個DebugLogger物件即可,提供的作為日誌過濾器的Func<string, LogLevel, bool>物件在自身的建構函式中由對應的引數指定。
1: public class DebugLoggerProvider : ILoggerProvider, IDisposable
2: {
3: private readonly Func<string, LogLevel, bool> _filter;
4: public DebugLoggerProvider(Func<string, LogLevel, bool> filter)
5: {
6: _filter = filter;
7: }
8:
9: public ILogger CreateLogger(string name)
10: {
11: return new DebugLogger(name, _filter);
12: }
13:
14: public void Dispose()
15: {}
16: }
針對DebugLoggerProvider的註冊可以通過如下三個針對ILoggerFactory介面的擴充套件方法AddDebug來完成。我們呼叫這些方法時可以為註冊的DebugLoggerProvider指定作為日誌過濾器的Func<string, LogLevel, bool>物件,也可以指定一個最低的日誌等級。如果這兩者都沒有指定,從給出的程式碼片段可以看出該方法會預設將Information作為最低日誌等級。也就是說,當我們呼叫AddDebug方法時如果沒有指定任何日誌過濾條件,等級為Debug的日誌訊息並不會被記錄下來,這一點也是我們個人覺得不太合理的地方。
1: public static class DebugLoggerFactoryExtensions
2: {
3: public static ILoggerFactory AddDebug(this ILoggerFactory factory)
4: {
5: return factory.AddDebug(LogLevel.Information);
6: }
7:
8: public static ILoggerFactory AddDebug(this ILoggerFactory factory, LogLevel minLevel)
9: {
10: return factory.AddDebug((cate, level) => level >= minLevel);
11: }
12:
13: public static ILoggerFactory AddDebug(this ILoggerFactory factory, Func<string, LogLevel, bool> filter)
14: {
15: factory.AddProvider(new DebugLoggerProvider(filter));
16: return factory;
17: }
18: }
接下來我們通過一個簡單的例項來演示針對DebugLogger的日誌記錄。我們建立一個空的控制檯應用,在新增必要的依賴之後,我們在Main方法中編寫了如下一段程式。如下面的程式碼片段所示,我們採用依賴注入的方式建立了一個LoggerFactory,並呼叫AddDebug方法完成了針對DebugLoggerProvider的註冊。在利用LoggerFactory創建出Logger物件之後,我們利用後者記錄了三條日誌訊息。
1: public class Program
2: {
3: public static void Main(string[] args)
4: {
5: ILogger logger = new ServiceCollection()
相關推薦
.NET Core的日誌[3]:將日誌寫入Debug視窗
定義在NuGet包“Microsoft.Extensions.Logging.Debug”中的DebugLogger會直接呼叫Debug的WriteLine方法來寫入分發給它的日誌訊息。如果需要使用DebugLogger來寫日誌,我們需要將它的提供者DebugLoggerProvider註冊到LoggerFa
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的日誌[2]:將日誌輸出到控制檯
對於一個控制檯應用,比如採用控制檯應用作為宿主的ASP.NET Core應用,我們可以將記錄的日誌直接輸出到控制檯上。針對控制檯的Logger是一個型別為ConsoleLogger的物件,ConsoleLogger對應的LoggerProvider型別為ConsoleLoggerProvider,這兩個型別都
.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"
用 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
.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擴充套件庫功能進行了簡單的介紹,從這一篇文章開始,我將逐步介紹擴充套件庫中的核心功能。 日誌作為非業務的通用領域基礎功能,有非常多的技術實現,這些第三方庫避免了我們花費時間去重複實現,
菜鳥入門【ASP.NET Core】3:準備CentOS和Nginx環境
sysconf service www post 輸入密碼 mct cnblogs kdt 提示 基本軟件 VMware虛擬機 centos:http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_
asp.net core 2.1 將控制器抽離到類庫中
cat start service public 網站 類庫 class all app startup.cs的ConfigureServices中添加: public void ConfigureServices(IServiceCollection
CentOS&.NET Core初試-3-Nginx的安裝和配置
新建 集群 防火墻 解決 http服務器 head nec def rem Nginx簡介 ??Nginx是一個免費的,開源的,高性能的HTTP服務器和反向代理,以及IMAP / POP3代理服務器。 ??Nginx以其高性能,穩定性,豐富的功能集,簡單的配置和低資源消耗而
Asp.net core實戰3: Asp.net core補充介紹
1.Asp.net core是可以執行在.Net Core框架上的,也可以執行在.Net Framework上. Asp.Net 只能執行在.Net Framework上 2.Asp.net core內部是有一個HTTP伺服器的,叫做Kestrel,
ASP.NET Core 實戰:將 .NET Core 2.0 專案升級到 .NET Core 2.1
一、前言 最近一兩個星期,加班,然後回去後弄自己的部落格,把自己的電腦從 Windows 10 改到 Ubuntu 18.10 又弄回 Windows 10,原本計劃的學習 Vue 中生命週期的相關知識目前也沒有任何的進展,嗯,罪過罪過。看了眼時間,11月也快要結束了,準備補上一
asp.net core 系列 3 依賴注入
一. 依賴注入概述 在軟體設計的通用原則中,SOLID是非常流行的縮略語,它由5個設計原則的首字母構成:單一原則(S)、開放封閉原則(O)、里氏替換原則(L)、介面分離原則(I)、依賴反轉原則(D)。本篇介紹依賴反轉原則以及在ASP.NET Core中的實現。 直接依賴是指:當一個類需要另一個類協
asp.net core 系列 3 依賴註入
solid zh-cn block 自帶 控制 架構設計 ado isp html 一. 依賴註入概述 在軟件設計的通用原則中,SOLID是非常流行的縮略語,它由5個設計原則的首字母構成:單一原則(S)、開放封閉原則(O)、裏氏替換原則(L)、接口分離原則(I)、依賴