01、spdlog源碼閱讀——registry註冊表

来源:https://www.cnblogs.com/dbai/archive/2023/07/30/17592392.html
-Advertisement-
Play Games

註冊表具有唯一標識,用於管理多個日誌 ```c++ // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org ...


註冊表具有唯一標識,用於管理多個日誌

// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

// Loggers registry of unique name->logger pointer
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe

#include <spdlog/common.h>
#include <spdlog/details/periodic_worker.h>

#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <mutex>

namespace spdlog {
class logger;

namespace details {
class thread_pool;

class SPDLOG_API registry
{
public:
    using log_levels = std::unordered_map<std::string, level::level_enum>;
    // 拷貝構造函數進行刪除(delete)的聲明。這意味著該類的拷貝構造函數被禁用,不允許進行拷貝構造。
    registry(const registry &) = delete;
    // 這意味著該類的賦值運算符重載被禁用,不允許進行對象之間的賦值操作。
    registry &operator=(const registry &) = delete;

    // 向全局的日誌器註冊表中註冊一個新的日誌器。
    void register_logger(std::shared_ptr<logger> new_logger);
    // 初始化一個新的日誌器並將其設置為【全局預設日誌器】。
    void initialize_logger(std::shared_ptr<logger> new_logger);
    // 通過名稱獲取logger
    std::shared_ptr<logger> get(const std::string &logger_name);
    // 獲取預設的全局日誌器。
    std::shared_ptr<logger> default_logger();

    // Return raw ptr to the default logger.
    // To be used directly by the spdlog default api (e.g. spdlog::info)
    // This make the default API faster, but cannot be used concurrently with set_default_logger().
    // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
    // 獲取預設的全局日誌器的裸指針(raw pointer)。
    // 裸指針是指向對象的普通指針,不包含智能指針的管理功能,需要手動管理對象的生命周期。
    logger *get_default_raw();

    // set default logger.
    // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
    // 設置新的預設全局日誌器
    void set_default_logger(std::shared_ptr<logger> new_default_logger);

    // 設置日誌器的線程池。
    // 通過設置日誌器的線程池,可以讓日誌器在後臺線程中執行寫入日誌的操作,從而減少主線程的阻塞時間,提高程式的性能和響應性。
    // 如果日誌記錄是同步進行的(預設情況下),日誌消息的寫入操作會在主線程中直接完成,可能會對程式的性能產生影響。
    // 而設置了線程池後,日誌消息的寫入操作將在後臺線程中進行,不會阻塞主線程的執行。
    void set_tp(std::shared_ptr<thread_pool> tp);

    // 獲取線程池
    std::shared_ptr<thread_pool> get_tp();

    // Set global formatter. Each sink in each logger will get a clone of this object
    // 設置日誌器的日誌消息格式化器
    // 日誌消息格式化器是用於將日誌消息的內容按照特定的格式進行格式化的組件
    // 通過設置日誌器的格式化器,可以自定義日誌消息輸出的樣式,例如包含時間戳、日誌級別、日誌內容等信息,並將格式化後的日誌消息寫入到輸出目標(如控制台、文件等)。
    void set_formatter(std::unique_ptr<formatter> formatter);

    // 啟用日誌器的回溯(backtrace)功能。
    void enable_backtrace(size_t n_messages);

    // 關閉日誌器的回溯(backtrace)功能。
    void disable_backtrace();

    // 設置日誌的級別
    void set_level(level::level_enum log_level);

    // 日誌器的日誌刷新級別。
    // 日誌刷新級別指定了日誌器在哪個日誌級別及以上時需要立即刷新日誌消息。
    // 如果調用 flush_on(spdlog::level::err),它會設置日誌器在 err(錯誤)級別及以上時立即刷新日誌消息。
    // 這意味著處於 err、critical 和 off 級別的日誌消息將在寫入後立即刷新,而低於這些級別的日誌消息(例如 info 或 debug)可能會被緩衝以提高效率。
    void flush_on(level::level_enum log_level);

    // 設置定時刷新日誌的時間間隔。
    // 支持不同類型的時間間隔。Rep 和 Period 是時間間隔類型的模板參數,用於指定時間間隔的數值和時間單位。
    // std::chrono::duration 是 C++ 標準庫中的時間間隔類模板,用於表示一段時間。
    // std::lock_guard<std::mutex> lock(flusher_mutex_);這是一個互斥鎖 std::mutex 的鎖保護,用於保證在設置定時器時,其他線程不會同時進行刷新操作。
    // 回調函數是 this->flush_all(),表示要執行日誌器的刷新操作。
    template<typename Rep, typename Period>
    void flush_every(std::chrono::duration<Rep, Period> interval)
    {
        std::lock_guard<std::mutex> lock(flusher_mutex_);
        auto clbk = [this]() { this->flush_all(); };
        // 這是一個定時器的成員變數,用於設置定時器,並將回調函數和時間間隔傳遞給 periodic_worker 構造函數。
        // periodic_worker 是 spdlog 庫中的一個類,用於創建周期性定時器。
        periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
    }

    // 設置錯誤處理函數(Error Handler)。
    // 這是一個函數指針或函數對象參數,用於表示錯誤處理函數。錯誤處理函數是一個用戶定義的函數,用於處理在 spdlog 庫中可能發生的錯誤情況
    // using err_handler = std::function<void(const std::string &err_msg)>;是一個函數對象類型,用於表示一個接受 const std::string & 類型參數的無返回值函數。
    void set_error_handler(err_handler handler);

    // 對所有已註冊的日誌器應用一個指定的函數。
    // 對所有已創建的日誌器進行一些特定的操作,例如更改日誌器的配置、設置日誌級別等。
    void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);

    // 將所有已註冊的日誌器的緩衝日誌消息刷新到輸出目標。
    void flush_all();

    // 從日誌器註冊表中移除指定名稱的日誌器。
    void drop(const std::string &logger_name);

    // 從日誌器註冊表中移除所有日誌器。
    void drop_all();

    // clean all resources and threads started by the registry
    // 關閉所有已註冊的日誌器並釋放相關資源。
    void shutdown();

    // 獲取一個遞歸互斥鎖(recursive mutex)對象的引用。
    // 遞歸互斥鎖是一種特殊類型的互斥鎖,允許同一線程多次對互斥鎖進行加鎖,而不會導致死鎖。
    std::recursive_mutex &tp_mutex();

    // 指定是否啟用自動註冊日誌器功能。
    // 為true時,通過 spdlog::create 創建的日誌器將自動註冊到全局的日誌器註冊表中,方便後續的查找和使用。
    // 為false時,通過 spdlog::create 創建的日誌器將不會自動註冊到日誌器註冊表中,需要手動調用其他函數(如 spdlog::register_logger)將其註冊
    void set_automatic_registration(bool automatic_registration);

    // set levels for all existing/future loggers. global_level can be null if should not set.
    // 可以同時設置多個日誌器的日誌級別,並可以設置全局的日誌級別。這樣可以方便地統一管理多個日誌器的日誌級別,並靈活地控制日誌輸出的詳細程度。
    // using log_levels = std::unordered_map<std::string, level::level_enum>;多個日誌級別配置,map類型
    void set_levels(log_levels levels, level::level_enum *global_level);

    // 獲取日誌器註冊表的單例實例。
    // 日誌器註冊表是全局唯一的,為了方便使用,spdlog 使用了單例模式,即只允許存在一個註冊表實例。
    static registry &instance();

    // 根據環境變數設置日誌器的日誌級別。
    // 使用環境變數來配置日誌級別具有靈活性,因為它允許在不修改代碼的情況下更改日誌級別。
    // 通過在運行時設置環境變數,可以動態地調整日誌輸出的詳細程度,方便在不同的場景或部署環境下進行日誌記錄。
    void apply_logger_env_levels(std::shared_ptr<logger> new_logger);

private:
    registry();
    ~registry();
    // 用於檢查是否存在指定名稱的日誌器,併在存在時拋出異常。
    // 通過調用 throw_if_exists_ 函數,並傳遞一個日誌器的名稱作為參數,可以檢查註冊表中是否已經存在具有相同名稱的日誌器。
    // 如果存在,該函數會拋出異常,提示名稱衝突,防止創建重名的日誌器。
    void throw_if_exists_(const std::string &logger_name);
    // 將指定的日誌器註冊到日誌器註冊表中。
    void register_logger_(std::shared_ptr<logger> new_logger);
    // 從配置中設置日誌器的日誌級別。
    bool set_level_from_cfg_(logger *logger);
    // 對日誌器註冊表進行線程同步的互斥鎖。對日誌器刷新操作進行線程同步的互斥鎖。
    std::mutex logger_map_mutex_, flusher_mutex_;
    // 遞歸互斥鎖對象的聲明。
    std::recursive_mutex tp_mutex_;
    // 日誌無序映射
    std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
    // 保存多個日誌器的名稱及對應的日誌級別。
    log_levels log_levels_;
    // formatter 是一個抽象基類,用於控制日誌消息的格式化方式
    // 獨占指針
    // 由於 std::unique_ptr 具有獨占性,即同一時刻只有一個 std::unique_ptr 可以擁有一個資源
    // 因此 formatter_ 對象可以確保在其生命周期內,只有一個智能指針持有 formatter 對象。
    // 這樣可以避免多個指針同時對同一個資源進行管理,確保資源的正確釋放。
    std::unique_ptr<formatter> formatter_;
    // 全局日誌級別變數的聲明和初始化。預設為info
    spdlog::level::level_enum global_log_level_ = level::info;
    // 日誌級別變數的聲明和初始化。flush_level_ 變數用於控制日誌的刷新行為。
    level::level_enum flush_level_ = level::off;
    // 錯誤處理器變數的聲明。
    err_handler err_handler_;
    // 線程池
    std::shared_ptr<thread_pool> tp_;
    // periodic_worker表示一個周期性任務的執行器。
    // 在 spdlog 中,periodic_worker 類型可能表示一個周期性的定時任務執行器,用於定期執行某個操作。
    std::unique_ptr<periodic_worker> periodic_flusher_;
    // 預設日誌
    std::shared_ptr<logger> default_logger_;
    // 是否自動註冊,預設true
    bool automatic_registration_ = true;
    // 回溯級別預設為0
    size_t backtrace_n_messages_ = 0;
};

} // namespace details
} // namespace spdlog

#ifdef SPDLOG_HEADER_ONLY
#    include "registry-inl.h"
#endif

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

-Advertisement-
Play Games
更多相關文章
  • “莆仙小館”——莆田文化展示APP 文化展示程式目的在於應用科學技術助推家鄉優秀傳統文化的展示與交流。通過圖片、視頻、音頻等展示方式向用戶立體地展示一個文化城邦。傳統文化與科學技術的有效融合,順應了社會發展的需要。傳統文化與科學技術的有效融合是發展中國特色社會主義文化的客觀需要,是傳承中國優秀傳統文 ...
  • # 解決方案 使用`ngClass`和`ngStyle`可以進行樣式的綁定。 ## ngStyle的使用 ngStyle 根據組件中的變數, isTextColorRed和fontSize的值來動態設置元素的顏色和字體大小 ```HTML This text has dynamic styles b ...
  • 在本篇文章中,我們詳細介紹了 Flutter 進階的主題,包括導航和路由、狀態管理、非同步處理、HTTP請求和Rest API,以及數據持久化。這些主題在實際應用中都非常重要,幫助你構建更複雜、功能更強大的 Flutter 應用。 ...
  • 在SpringBoot的Controller中,可以使用註解@RequestBody來獲取POST請求中的JSON數據。我們可以將這個註解應用到一個Controller方法的參數上,Spring將會負責讀取請求正文中的數據,將其反序列化為一個Java對象,並將其作為Controller方法的參數傳遞 ...
  • 對於從事後端開發的同學來說,線程安全問題是我們每天都需要考慮的問題。 線程安全問題通俗地講主要是在多線程的環境下,不同線程同時讀和寫公共資源(臨界資源)導致的數據異常問題。 比如:變數a=0,線程1給該變數+1,線程2也給該變數+1。此時,線程3獲取a的值有可能不是2,而是1。線程3這不就獲取了錯誤 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 關於bean的作用域(scope) - 官方資料:ht ...
  • ## 開篇-為什麼要使用線程池? ​ Java 中的線程池是運用場景最多的併發框架,幾乎所有需要非同步或併發執行任務的程式都可以使用線程池。在開發過程中,合理地使用線程池能夠帶來 3 個好處。 ​ 第一:降低資源消耗。通過重覆利用已創建的線程降低線程創建和銷毀造成的消耗。 ​ 第二:提高響應速度。當任 ...
  • ## 實踐環境 Win10 Java JDK1.8 ## 代碼實現 pom.xml配置 ```xml 4.0.0 com.shouke example 1.0 1.8 ${java.version} ${java.version} 4.1.2 org.apache.poi poi-ooxml ${p ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...