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開始。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM