01、spdlog源码阅读——registry注册表

发布时间 2023-07-30 23:51:43作者: 摩天仑

注册表具有唯一标识,用于管理多个日志

// 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