spdlog源码阅读

spdlog源码阅读 ,第1张

spdlog源码阅读 (2): sinks的创建和使用 2. sink创建 2.1 还是rotating_file_sink

我们仍然以rotating_file_sink为例来说明在spdlog中sink的创建过程。




在spdlog-master/tests中能够找到file_log.cpp文件,其中有关于rotate的示例代码,如下:

TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
1. prepare_logdir();
2. std::string basename = "logs/rotating_log";
3. auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
//......
}

line: 3,创建名为"logger",文件名"rotating_log",在文件夹logs下,文件最大为1024(字节),

且只能有一个文件。


返回的是logger对象,并且它是支持多线程的(mt)。


2.2 create

继续查看rotating_logger_mt,如下:

inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
1. return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
}

line: 1, 实际创建对象。


create是一个模板函数,如下:

template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
{
2. sink_ptr sink = std::make_shared<Sink>(args...);
3. return details::registry::instance().create(logger_name, { sink });
}

Sink在本示例中即spdlog::sinks::rotating_file_sink_mt, 第二个模板参数也就是

rotating_file_sink_mt的构造函数参数。


针对每一种sink,都会存在一个对应的create函数。




当然sink的实际创建也是发生在该函数中(line2)。


2.3 单例registry

1.2中 line:3又继续调用了registry执行对象的创建,我们先来看下registry是个什么鬼

#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
#else
typedef registry_t<std::mutex> registry;
#endif

可以看出在多线程的情况下,类registry需要锁的支持。




下面来到最需要关心的位置,也就是最后的create, 代码如下:

template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
1. std::lock_guard<Mutex> lock(_mutex);
throw_if_exists(logger_name);
std::shared_ptr<logger> new_logger;
2. if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end); 3. if (_formatter)
new_logger->set_formatter(_formatter); if (_err_handler)
new_logger->set_error_handler(_err_handler); new_logger->set_level(_level); //Add to registry
4. _loggers[logger_name] = new_logger;
return new_logger;
}
  • line1: 在多线程的情况下,创建一个logger对象需要加锁。


  • line2: 区分日志同步写/异步写,实际logger对象创建也是发生在这里。


  • line3: 有自定义格式时,使用自定义的格式。


  • line4: 将新创建的对象加入全局管理(registry是个单例),这也是需要加锁的原因。


2.4 小结

从上述的过程中,可以看到sink的实际创建是发生在create中,创建的sink对象做为参数参与了

logger对象的创建。


在logger中sink起到的作用是什么,就是下面要讨论的问题了。


3. sink的使用

仍然以2.1 的例子继续,看下当时省略的代码先:

TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
//......
for (int i = 0; i < 10; ++i)
1. logger->info("Test message {}", i);
//......
}

line1: 调用info输出日志,跟踪代码看下info到底是做了什么。


//片段1
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
1. _sink_it(log_msg);
} //片段2
inline void spdlog::logger::_sink_it(details::log_msg& msg)
{
_formatter->format(msg);
2. for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
{
3. sink->log(msg);
}
} if(_should_flush_on(msg))
flush();
}
  • line1: 创建一个log_msg对象,并将msg存入到该对象中,执行_sink_it
  • line2: 遍历当前对象中所有的sink,并对每一个sink执行它的log函数。


到这里,我们已经和上篇中的sink衔接上了。


3.1 小结

本篇已经接近尾声,从之前的分析中,可以得到如下的关键信息:

  1. log_msg是spdlog真正存储日志信息的位置,自然也是最后的输出对象。


  2. sink通过base_sink实现对mt/st的区分。


  3. registry是存储logger对象的单例,而logger是日志输出的真正执行者。


  4. spdlog通过创建不同的sink决定最后实例化的logger是mt/st,也能够决定最后的输出目标。


  5. 用户自定义的格式在logger中_sink_it时生效。


下一篇我们从log_msg开始。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/587748.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存