那些年我們用過的組件-結構化日誌組件 Serilog

来源:https://www.cnblogs.com/newton/archive/2023/01/05/17026903.html
-Advertisement-
Play Games

什麼是結構化日誌 我們記錄日誌慣常使用 log4j2、NLog 等日誌組件,這些組件提供了輸出到多種終端的能力,但是大部分時候我們選擇將日誌輸出到操作系統的文件系統中,為什麼呢?至少有一部分原因是記錄的每條日誌為字元串格式,且按時間由遠往進順序記錄,打開文件可以直接人肉檢索;如果這些日誌記錄到其它終 ...


什麼是結構化日誌

我們記錄日誌慣常使用 log4j2NLog 等日誌組件,這些組件提供了輸出到多種終端的能力,但是大部分時候我們選擇將日誌輸出到操作系統的文件系統中,為什麼呢?至少有一部分原因是記錄的每條日誌為字元串格式,且按時間由遠往進順序記錄,打開文件可以直接人肉檢索;如果這些日誌記錄到其它終端比如資料庫中,由於是字元串格式,無法依靠資料庫的機制提高檢索效率,反而日誌的頻繁寫入和數據量的持續增大,對資料庫造成很大壓力,還需要花時間調優資料庫結構。

但 22 世紀都快到了,還在用古老的人肉檢索實在說不過去,於是出現了流行一時的 EFKELK框架,它們是幾個組件的集合。大致流程如下:

  1. 首先是日誌採集組件比如 filebeats,定時從配置好的路徑中採集增量日誌;
  2. 上傳到消息隊列比如 kafka,緩解日誌過多時的傳輸壓力;
  3. 然後送達日誌處理組件比如 logstash, logstash 使用 filter 對日誌進行拆分、映射、過濾等,抽取關鍵內容並形成符合目標資料庫特性的格式。註意此處出來的就是結構化日誌;
  4. 將結構化日誌存儲到特定的資料庫比如 elasticsearch 中;
  5. 通過用戶界面如 Kibana 進行日誌檢索。

上述流程在不同場景下有一些變種,不再贅述。 它們的主要目的就是使得傳統的文件日誌可以被電腦高效檢索。

那麼有沒有一種可能,跳過文件存儲,直接將日誌按特定格式寫入到目標存儲容器,可能是 elasticsearch,也可能是 mysql,甚至是文件系統。同樣代碼,輸出不同的格式到不同的終端,同時滿足 human-friendly and machine-readable

在 .NET 世界中, 本文的主角 Serilog 就可以幫我們省去那些彎彎繞繞,依靠它,記錄與查詢日誌顯得簡單而純粹。

Serilog

以官方例子說明:

var position = new { Latitude = 25, Longitude = 134 };
var elapsedMs = 34;

log.Information("Processed {@Position} in {Elapsed} ms", position, elapsedMs);

按字面意思,最終會輸出:

09:14:22 [INF] Processed {"Latitude": 25, "Longitude": 134} in 34 ms.

當 Serilog 將日誌直接輸出到文件系統或命令行時,結果是這樣沒錯,其它日誌組件也能做到(廢話)。

當輸出到 MongoDB 時,結果就不一樣了:

{ "Position": { "Latitude": 25, "Longitude": 134 }, "Elapsed": 34 }

Sink

Serilog 將輸出目標稱之為 sink,不同的 sink 可以有各自的格式要求。其實原理很簡單,輸出到特定 sink 時,日誌對象會先格式化處理(註意不是先生成字元串再格式化)。Serilog.Formatting.Compact 就是格式化為 json 的類庫,輸出到 elasticsearch 還需要 Serilog.Formatting.Elasticsearch。不過除非自定義 sink,這些我們都不用關心,使用時只要引入需要的 sink 類庫即可。

使用

下麵介紹在 .NET6 中使用 Serilog。

先引入 Serilog 類庫和需要的 Sink 庫比如這裡的 Serilog.Sinks.File

<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

以通用宿主程式為例:

IHost host = Host.CreateDefaultBuilder(args).Build();

// 配置並創建 logger 實例
var log = new LoggerConfiguration()
    .MinimumLevel.Warning()
    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10485760, rollOnFileSizeLimit: true, retainedFileCountLimit: 100, buffered: true)
    .CreateLogger();

log.Information("Hello, Serilog!"); // 直接使用(可以創建多個實例使用)

Log.Logger = log;   // Serilog 並沒有實例狀態需要線程間維護,所以為了方便我們可以使用單例模式,將實例賦給全局靜態屬性
Log.Information("The global logger has been configured");   // 項目內任意其它地方均可使用

await host.RunAsync().ContinueWith(_=> Log.CloseAndFlush());    // app 退出時釋放 logger 占用資源

如果想以 .NET 內置的方式調用 Serilog,對於通用宿主程式,須引入 Serilog.Extensions.Hosting,其扮演適配器的角色,將 Serilog 自己的介面 Serilog.ILogger 轉換為 Microsoft.Extensions.Logging.ILogger 使用。如果是 web 項目的話,引入的是 Serilog.AspNetCore.NET Core 1.0, 1.1 等版本需要引入的是 Serilog.Extensions.Logging

更改後的版本如下:

IHost host = Host
    .CreateDefaultBuilder(args)
    .UseSerilog()   // 新增該行
    .Build();

// ... 其餘代碼同上

另外,上述代碼是直接硬編碼配置 logger,更好的方式是通過 appsettings.json 配置 logger。首先引入 Serilog.Settings.Configuration,然後在 appsettings.json 中移除預設的 Logging 配置節,替換為 Serilog 配置節如下:

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": "Warning",
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "Logs/log.txt",
          "rollingInterval": "Day",
          "fileSizeLimitBytes": 10485760,
          "rollOnFileSizeLimit": true,
          "retainedFileCountLimit": 100,
          "buffered": true
        }
      }
    ]
  }
}

代碼更改如下:

IHost host = Host
    .CreateDefaultBuilder(args)
    .UseSerilog((ctx, config) => config
        .ReadFrom.Configuration(ctx.Configuration))
    .Build();

//以下註釋
//var log = new LoggerConfiguration()
//    .MinimumLevel.Warning()
//    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day, fileSizeLimitBytes: 10485760, rollOnFileSizeLimit: true, retainedFileCountLimit: 100, shared: true, buffered: true)
//    .CreateLogger();
//Log.Logger = log;

await host.RunAsync(); //註釋.ContinueWith(_ => Log.CloseAndFlush());

採用這種方式,Log.Logger 會隱式賦值,併在系統退出時自動釋放資源。

參考資料

Docker+EFK 快速搭建日誌收集系統
Message Templates
.NET Worker Service 添加 Serilog 日誌記錄


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

-Advertisement-
Play Games
更多相關文章
  • RESTfulL是一種網路應用程式的設計風格和開發方式,即介面請求方式和路徑的一種風格。 普通風格: localhost:8080/add?a=1&b=2 RestFul風格: localhost:8080/add/1/2 GET 獲取: localhost:8080/item/1 POST 新增: ...
  • 1、粘包與半包 啥也不說了,直接上代碼是不是有點不太友好,我所謂了,都快過年了,還要啥自行車 我上來就是一段代碼猛如虎 1.1 伺服器代碼 public class StudyServer { static final Logger log = LoggerFactory.getLogger(Stu ...
  • 我的客服系統有一些介面是專門給內部調用的,只允許其他內部系統來調用,不允許隨意訪問,可以使用IP白名單機制 使用 Gin 框架實現 IP 白名單機制可以使用中間件的方式實現。你可以編寫一個中間件函數,在每個請求到來時檢查它的 IP 地址是否在白名單中,如果不在,則返回錯誤信息。 例如,你可以這樣編寫 ...
  • JetBrains Clion 是一款專為 C/C++ 開發所設計的跨平臺 IDE。本文適用 JetBrains CLion v2019.3/3.1/3.2/3.3 永久激活,附破解補丁和激活碼,可以永久激活 Windows、MAC、Linux 下的 CLion!!!網上有激活碼的激活方式(更改 h ...
  • go build 命令好處 我開發了一套線上客服系統源碼,使用了go build進行編譯 在我的線上客服系統使用 go build 命令的主要好處是,它可以將 Go 程式編譯成可執行文件,這樣就可以將程式部署到生產環境中。 在生產環境中運行的程式通常是編譯後的可執行文件,因為這樣可以提高程式的執行效 ...
  • 可迭代對象通過iter(),轉化為迭代器對象,迭代器可以使用next()訪問,可迭代對象不能直接使用next(); 迭代器是一個可以記住遍歷的位置的對象,所以可以方便的使用next()。 可迭代對象(iterable):凡是具有__iter__的方法的類,都是可迭代的類。可迭代類創建的對象實現了__ ...
  • ​ 最近的資料庫課程要求將MySQL資料庫部署在伺服器上,參考了大佬們的博客後,總結一下。 先放上參考的大佬們的博客。 【原創經驗分享】JQuery(Ajax)調用WCF服務 - 南宮蕭塵 - 博客園 (cnblogs.com) WinForm+WCF+mysql+http實現簡單的用戶登錄註冊_小 ...
  • 在ERP系統中,採集一線的生產數據是重要工作之一,而稱重計量是企業的核心資產數據,人工計重費時費力,還容易出錯,重量數據是否正確,直接影響企業的採購或銷售額。基於此,由系統對接電子秤實現自動抓取數據是企業管理的第一步。 電子秤,一般由重量感測器、砝碼、底座、儀錶等組成。儀錶與感測器相連,儀錶一般具有 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...