【AspNetCore源碼】設計模式 - 提供者模式

来源:https://www.cnblogs.com/WilsonPan/archive/2020/04/28/12793220.html
-Advertisement-
Play Games

今天看AspNetCore源代碼發現日誌模塊的設計模式(提供者模式)。 設計模式的好處是,我們可以容易擴展它達到我們要求,除了要知道如何擴展它,還應該在其他地方應用它, ...


AspNetCore源代碼發現日誌模塊的設計模式(提供者模式),特此記錄

學習設計模式的好處是,我們可以容易擴展它達到我們要求,除了要知道如何擴展它,還應該在其他地方應用它

 

類圖 & 分析

角色分析

日誌工廠 ( LoggerFactory --> ILoggerFactory)

- 提供註冊提供者

- 創建日誌記錄器(Logger)

 

日誌記錄器(Logger --> ILogger)

- 寫入日誌記錄(遍歷所有日誌提供者的Logger)

- 這裡所有註冊的日誌提供者聚合

 

日誌提供者(ConsoleLoggerProvider --> ILoggerProvider)

- 創建具體日誌記錄器

 

具體日誌記錄者(ConsoleLogger,EventLogLogger)

- 將日誌寫入具體媒介(控制台,Windows事件日誌)

 

現在來看看這個模式

1. 提供標準的日誌寫入介面(ILogger)

2. 提供日誌提供者介面(ILoggerProvider)

3. 提供註冊提供者介面(ILoggerFactory.AddProvider)

 

這裡只是列出部分類和方法,整個Logging要比這個還多,為什麼寫個日誌要整那麼多東西?

程式唯一不會變就是不斷在變化,這個也是為什麼要設計模式運用到程式當中的原因,讓程式可擴展來應對這種變化。

 

AspNetCore內置 8種日誌記錄提供程式 ,但肯定還是遠遠不夠,因為有的可能想把日誌寫在文本,有的想寫在Mongodb,有的想寫在ElasticSearch等等,Microsoft不可能把所有的都實現,就算實現也未必適合你的業務使用。

 

假設現在需要把日誌寫在Mongo,只需要

1. 實現Mongodb的ILogger - 將日誌寫到Mongodb

2. 實現Mongodb的ILoggerProvider - 創建Mongodb的Logger

3. 把Provider註冊到AspNetCore - ILoggerFactory.AddProvider

 

這裡都是新增代碼達到實現把日誌寫入到Mongodb,這就是6大設計原則之一對擴展開放(可以添加自己的日誌),對修改封閉(不需要修改到內部的方法)

 

AspNetCore代碼實現(只列出介面)

ILoggerFactory

ILogger CreateLogger(string categoryName);
void AddProvider(ILoggerProvider provider);

 CreateLogger : 這個和ILoggerProvider提供的CreateLogger雖然都是現實ILogger介面,但是做的事情不一樣,LoggerFactory創建的是Logger實例,裡面聚合了具體寫日誌的Logger,遍歷它們輸出。

categoryName : 可以指定具體,若使用泛型相當於typeof(T).FullName,這個用於篩選過濾日誌

 AddProvider : 註冊一個新的提供者,然後遍歷現有的Logger,把新的Provider添加到現有logger裡面

 

ILoggerProvider

ILogger CreateLogger(string categoryName);

 CreateLogger : 用於創建具體寫日誌Logger(例如Console)

 

ILogger

void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
bool IsEnabled(LogLevel logLevel);
IDisposable BeginScope<TState>(TState state);

Log<TState>(....): 輸出日誌

bool IsEnabled : 指定的日誌級別是否可用

IDisposable BeginScope<TState>() : 開啟日誌作用域,將這個域範圍的日誌都放一起

 

AspNetCore使用第三方日誌組件(Log4Net)

 

AspNetCore使用Log4Net作為記錄很簡單,只需

1. 安裝包:

dotnet install Microsoft.Extensions.Logging.Log4Net.AspNetCore

2. Configure 添加:

loggerFactory.AddLog4Net();

3. 添加log4net.config配置文件

 

看看Microsoft.Extensions.Logging.Log4Net.AspNetCore如何實現ILogger和ILoggerProvider介面(代碼有截取)

Log4NetProvider

public ILogger CreateLogger(string categoryName)
    => this.loggers.GetOrAdd(categoryName, this.CreateLoggerImplementation);

private Log4NetLogger CreateLoggerImplementation(string name)
{
    var options = new Log4NetProviderOptions
    {
        Name = name,
        LoggerRepository = this.loggerRepository.Name
    };

    options.ScopeFactory = new Log4NetScopeFactory(new Log4NetScopeRegistry());

    return new Log4NetLogger(options);
}

 

Log4NetLogger

switch (logLevel)
{
    case LogLevel.None:
        break;
    case LogLevel.Critical:
        {
            string overrideCriticalLevelWith = options.OverrideCriticalLevelWith;
            if (!string.IsNullOrEmpty(overrideCriticalLevelWith) && overrideCriticalLevelWith.Equals(LogLevel.Critical.ToString(), StringComparison.OrdinalIgnoreCase))
            {
                log.Critical(text, exception);
            }
            else
            {
                log.Fatal(text, exception);
            }
            break;
        }
    case LogLevel.Debug:
        log.Debug(text, exception);
        break;
    case LogLevel.Error:
        log.Error(text, exception);
        break;
    ......
}

log4net的ILog是沒有Trace和Critical方法,這兩個是擴展方法,調用log4net log4net.Repository.Hierarchy.Logger.Log()方法

log4net 裡面有Fatal代表日誌最高級別,AspNetCore的Critical是日誌最高級別,習慣log4net可能習慣用Fatal,這個時候只需要在註冊的時候

loggerFactory.AddLog4Net(new Log4NetProviderOptions()
{
    OverrideCriticalLevelWith = "Critical"
});

在Controller調用

 _logger.LogCritical("Log Critical");

看看效果

2020-04-27 13:42:05,042 [10] FATAL LoggingPattern.Controllers.WeatherForecastController (null) - Log Critical

 

奇怪,沒有按預期發生。這個組件是開源的,可以下載下來調試看看,github克隆下來 Microsoft.Extensions.Logging.Log4Net.AspNetCore

 

調試過程

1. 將Microsoft.Extensions.Logging.Log4Net.AspNetCore.csproj的SignAssembly設置false(這個是程式集強簽名)

<SignAssembly>false</SignAssembly>

 2. 將引用改成引用本地,我這裡是放在跟項目平級

  <ItemGroup>
    <ProjectReference Include="..\Microsoft.Extensions.Logging.Log4Net.AspNetCore\src\Microsoft.Extensions.Logging.Log4Net.AspNetCore\Microsoft.Extensions.Logging.Log4Net.AspNetCore.csproj" />
  </ItemGroup>

 

我這裡是用VSCode,如果用VS不用這麼麻煩

3. 然後就可以打斷點,在寫日誌和之前看到的那個判斷打個斷點

4. 接下來就是看看這個值怎麼來的

builder.Services.AddSingleton<ILoggerProvider>(new Log4NetProvider(options));

public Log4NetProvider(Log4NetProviderOptions options)
{
}

 

註冊一個單例的Log4NetProvider,參入參數options,Logger是在Provider的CreateLogger創建,現在看看CreateLogger

public ILogger CreateLogger(string categoryName)
    => this.loggers.GetOrAdd(categoryName, this.CreateLoggerImplementation);

private Log4NetLogger CreateLoggerImplementation(string name)
{
    var options = new Log4NetProviderOptions
    {
        Name = name,
        LoggerRepository = this.loggerRepository.Name
    };
    options.ScopeFactory = new Log4NetScopeFactory(new Log4NetScopeRegistry());
    return new Log4NetLogger(options);
}

 

到這裡就清楚了,CreateLoggerImplementation裡面又new了一個options,然後沒有給OverrideCriticalLevelWith賦值(我認為這是個Bug,應該也很少人會用這個功能)這裡之所以沒用單例的options,因為要給每個Logger的目錄名稱動態賦值。

給這個庫作者提了Issues和PR

 

添加自定義的日誌記錄器

 

假設現在需要把日誌加入到Mongodb,只需完成下麵幾個步驟

1. 添加Mongodb驅動,(dotnet-cli)

dotnet add package MongoDB.Driver

2. 實現介面ILogger

public class MongodbLogger : ILogger
{
    private readonly string _name;
    private MongoDB.Driver.IMongoDatabase _database;

    public MongodbLogger(string name, MongoDB.Driver.IMongoDatabase database)
    {
        _name = name;
        _database = database;
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        var collection = _database.GetCollection<dynamic>(logLevel.ToString().ToLower());

        string message = formatter(state, exception);

        collection.InsertOneAsync(new
        {
            time = DateTime.Now,
            name = _name,
            message,
            exception
        });
    }
    public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;

    public System.IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
}

 3. 實現ILoggerProvider介面

public class MongodbProvider : ILoggerProvider
{
    private readonly ConcurrentDictionary<string, MongodbLogger> _loggers = new ConcurrentDictionary<string, MongodbLogger>();
    private MongoDB.Driver.IMongoDatabase _database;
    public MongodbProvider(MongoDB.Driver.IMongoDatabase database)
    {
        _database = database;
    }
    public ILogger CreateLogger(string categoryName)
        => _loggers.GetOrAdd(categoryName, name => new MongodbLogger(categoryName, this._database));
    public void Dispose() => this._loggers.Clear();
}

 4. 添加MongodbLogging擴展函數(非必須)

public static ILoggerFactory AddMongodb(this ILoggerFactory factory, string connetionString = "mongodb://127.0.0.1:27017/logging")
{
    var mongoUrl = new MongoDB.Driver.MongoUrl(connetionString);
    var client = new MongoDB.Driver.MongoClient(mongoUrl);

    factory.AddProvider(new MongodbProvider(client.GetDatabase(mongoUrl.DatabaseName)));

    return factory;
}

 5. Configure註冊MongodbLogging

loggerFactory.AddMongodb();

運行效果

 

擴展

   

  設計模式的好處是,我們可以容易擴展它達到我們要求,除了要知道如何擴展它,還應該在其他地方應用它,例如我們經常需要消息通知用戶,但是通知渠道(提供者),在一開始未必全部知道,例如一開始只有簡訊,郵件通知,隨著業務發展可能需要增加微信推送,提供者模式就很好應對這一種情況,很容易畫出下麵類圖。

當需要擴展發送消息渠道,只需要實現ISenderProvider(哪個提供),ISender(如何發送)

 

當需要擴展發送消息渠道,只需要實現ISenderProvider(哪個提供),ISender(如何發送)

轉發請標明出處:https://www.cnblogs.com/WilsonPan/p/12793220.html

示例代碼:https://github.com/WilsonPan/AspNetCoreExamples/tree/master/LoggingPattern


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 搬運自:https://www.cnblogs.com/AlanLee/p/5329555.html 原理搜關鍵字:DFA演算法 基本照抄了原文的JAVA代碼,其中應該可以用Dictionary<string,int>來代替Hashtable,但搜到的資料都說Hashtable快得要命,雖然知道他們說 ...
  • 前言 CQRS ( Command Query Responsibility Segregation )命令查詢職責分離模式,它主要從我們業務系統中進行分離出我們(Command 增、刪、改)和(Query 查), 同時他可以明確的區分我們每一個動作向我們的請求模型和響應模型.從而降低了我們系統的復 ...
  • [TOC] 上一篇,我們學習了任務的基礎,學會多種方式場景任務和執行,非同步獲取返回結果等。上一篇講述的知識比較多,這一篇只要是代碼實踐和示例操作。 判斷任務狀態 | 屬性 | 說明 | | | | | IsCanceled | 獲取此 Task 實例是否由於被取消的原因而已完成執行。 | | IsC ...
  • 是否能編譯一個exe , 又能啟動 Asp.Net Core , 又能自帶最新版的Chromium瀏覽器 , 自己瀏覽自己? 這裡給出了一個最簡單的C#/C++整合CEF框架的方式和詳細步驟. ...
  • 題外話: 接上篇吧。上篇主要是介紹下LayUI的使用,太興奮手打代碼去了- -。 其實這個項目用了Unity這個IOC框架,畢竟人要有夢想。感興趣的可以自己去瞭解下,後面有機會會說說對於IOC和DI的個人拙見以及Unity的使用。 進入正題。 二.WebApi 一個有夢想的鹹魚做的一個有夢想的dem ...
  • netcore數據連接配置 第一種 appsettings.json ConfigureServices 第二種 ConfigureServices ...
  • C 多態性 多態性常被視為自封裝和繼承之後,面向對象的編程的第三個支柱。 Polymorphism(多態性)是一個希臘詞,指“多種形態”, 多態: 一個介面多個功能。 靜態多態性: 編譯時發生函數響應(調用); 動態多態性: 運行時發生函數響應。 靜態綁定(早期綁定): 編譯時函數和對象的連接機制。 ...
  • 目 錄 1. 現象概述... 1 2. 操作資料庫的代碼... 2 3. 引起的異常... 2 4. 異常信息分析... 3 5. 分析結論猜測... 3 1. 現象概述 .NETCore的Console和AspNetCore程式對資料庫進行操作,通過析構函數(Finalize)釋放資料庫連接資源, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...