spdlog源码阅读(三):Register类的实现

spdlog 日志库中,register 类是为了管理logger的生命周期并提供方便的全局访问。该类包括以下功能。

单例实现

registry类的构造函数设置成private,并将拷贝构造函数和赋值运算符设置成delete,这是为了避免通过非期望的方式创建registry。registry只能通过instance方法对其实例化,该方法利用局部静态变量初始化的线程安全性来保证registry的初始化安全。

1
2
3
4
SPDLOG_INLINE registry &registry::instance() {
static registry s_instance;
return s_instance;
}

提供默认logger

Register在构造时会创建一个默认的logger,并提供get_default_raw方法来获取默认的logger对象的裸指针,再通过函数的封装,提供了不需要额外初始化的日志接口。

此外,还可以通过set_default_logger方法来修改默认的logger对象,并使用智能指针保存该对象,避免内存泄漏。

如果不需要使用默认logger可以使用SPDLOG_DISABLE_DEFAULT_LOGGER取消相关实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// registry构造函数
SPDLOG_INLINE registry::registry()
: formatter_(new pattern_formatter()) {
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
#ifdef _WIN32
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#else
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#endif

const char *default_logger_name = "";
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
loggers_[default_logger_name] = default_logger_;

#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
}

// 提供的不需要初始化的日志接口
template <typename... Args>
inline void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args) {
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
inline void trace(format_string_t<Args...> fmt, Args &&...args) {
default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
}

template <typename... Args>
inline void debug(format_string_t<Args...> fmt, Args &&...args) {
default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
}

// set_default_logger方法
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
if (new_default_logger != nullptr) {
loggers_[new_default_logger->name()] = new_default_logger;
}
default_logger_ = std::move(new_default_logger);
}

logger的统一管理

register提供了logger对象的管理方法,使用std::unordered_map来储存logger对象,并通过logger名称来对其索引。

通过工厂函数创建的logger对象会默认添加进管理,对于手动创建的logger对象可以使用register_logger_方法添加进管理。

register使用get方法来获取相应的logger对象,使用drop方法删除已管理的logger对象,还可以使用drop_all方法删除所有已管理的logger对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 工厂函数的create静态方法默认调用initialize_logger函数
struct synchronous_factory {
template <typename Sink, typename... SinkArgs>
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args) {
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
details::registry::instance().initialize_logger(new_logger);
return new_logger;
}
};

// initialize_logger函数内部调用了register_logger_,将logger对象添加进管理
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
new_logger->set_formatter(formatter_->clone());

if (err_handler_) {
new_logger->set_error_handler(err_handler_);
}

// set new level according to previously configured level or default level
auto it = log_levels_.find(new_logger->name());
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
new_logger->set_level(new_level);

new_logger->flush_on(flush_level_);

if (backtrace_n_messages_ > 0) {
new_logger->enable_backtrace(backtrace_n_messages_);
}

// 可以通过修改automatic_registration_禁用自动添加管理的功能
if (automatic_registration_) {
register_logger_(std::move(new_logger));
}
}

// 获取管理的logger对象
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto found = loggers_.find(logger_name);
return found == loggers_.end() ? nullptr : found->second;
}

// 删除已管理的logger对象
SPDLOG_INLINE void registry::drop(const std::string &logger_name) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto is_default_logger = default_logger_ && default_logger_->name() == logger_name;
loggers_.erase(logger_name);
if (is_default_logger) {
default_logger_.reset();
}
}

// 删除所有已管理的logger对象
SPDLOG_INLINE void registry::drop_all() {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
loggers_.clear();
default_logger_.reset();
}

register实现了一套logger一样的接口,以便实现全局logger设置。实现方法类似,都是遍历所有logger再调用其方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Set global formatter. Each sink in each logger will get a clone of this object
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
formatter_ = std::move(formatter);
for (auto &l : loggers_) {
l.second->set_formatter(formatter_->clone());
}
}

SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
backtrace_n_messages_ = n_messages;

for (auto &l : loggers_) {
l.second->enable_backtrace(n_messages);
}
}

SPDLOG_INLINE void registry::disable_backtrace() {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
backtrace_n_messages_ = 0;
for (auto &l : loggers_) {
l.second->disable_backtrace();
}
}

SPDLOG_INLINE void registry::set_level(level::level_enum log_level) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) {
l.second->set_level(log_level);
}
global_log_level_ = log_level;
}

SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) {
l.second->flush_on(log_level);
}
flush_level_ = log_level;
}

SPDLOG_INLINE void registry::set_error_handler(err_handler handler) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) {
l.second->set_error_handler(handler);
}
err_handler_ = std::move(handler);
}

SPDLOG_INLINE void registry::flush_all() {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) {
l.second->flush();
}
}

除此之外,还实现了apply_all方法,可以通过传函数对象,实现多样化的管理需求。

1
2
3
4
5
6
7
SPDLOG_INLINE void registry::apply_all(
const std::function<void(const std::shared_ptr<logger>)> &fun) {
std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) {
fun(l.second);
}
}

线程安全

register单例的实例化使用懒汉模式,并使用局部静态变量初始化来保证线程安全性。

1
2
3
4
SPDLOG_INLINE registry &registry::instance() {
static registry s_instance;
return s_instance;
}

对logger对象的操作之前使用RAII的锁来保证线程安全性

1
std::lock_guard<std::mutex> lock(logger_map_mutex_);

性能优化

频繁加锁会导致日志性能下降,因此register类除了default_logger方法还提供了get_default_raw方法,提供不加锁的获取默认logger指针的方法。由于不加锁,因此改方法非线程安全,使用时不能与set_default_logger方法同时使用。

作者

echo

发布于

2024-10-07

更新于

2024-10-13

许可协议

评论