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 小結
本篇已經接近尾聲,從之前的分析中,可以得到如下的關鍵信息:
- log_msg是spdlog真正存儲日志信息的位置,自然也是最后的輸出對象。
- sink通過base_sink實現對mt/st的區分。
- registry是存儲logger對象的單例,而logger是日志輸出的真正執行者。
- spdlog通過創建不同的sink決定最后實例化的logger是mt/st,也能夠決定最后的輸出目標。
- 用戶自定義的格式在logger中_sink_it時生效。
下一篇我們從log_msg開始。