spdlog 源碼解析
##記日誌兩種模式:
-
同步: 對於basic_file_sink, 日誌每次調用fwrite寫入到文件緩存,即使同步模式,也需要
flush_every
來定時flush,否則crash 時有丟失日誌風險 -
異步:
- log寫日誌就是把日誌入一個循環queue。
- 初始時會開啓一個線程池,裏面有n個線程,每個線程裏做的工作就是從循環隊列中取日誌,寫入到指定輸出設備。當隊列爲空時,取不到數據時,會wait 10s時間,然後繼續循環取數據。
- 也需要
flush_every
來定時flush 數據
sink
sink 類實現多態,方便擴展.
對於繼承的類,都是模板類
template<typename Mutex>
class base_sink : public sink
{
}
Mutex 可以空實現,但是這樣無法保證日誌的連貫有序性,打日誌的場景感覺實用性不大。但是是個很棒的設計方法。可以在其他場景運用這種設計方法。
可變模板參數
創建logger的代碼
- 外面創建logger的api,是個模板函數
auto my_logger = spdlog::basic_logger_mt<spdlog::default_factory>("file_logger1", "logs/basic-log.txt");
auto my_logger = spdlog::basic_logger_mt("file_logger1", "logs/basic-log.txt");//使用了默認的模板default_factory
創建logger的函數模板, 模板有默認值default_factory
template<typename Factory = default_factory>
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
{
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
}
其實可以寫成
return Factory::create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
寫成 Factory::template 這種形式還沒太明白這是什麼語法
第2步調用的create方法,此方法爲可變模板參數。
using default_factory = synchronous_factory;
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<logger>(std::move(logger_name), std::move(sink));
details::registry::instance().initialize_logger(new_logger);
return new_logger;
}
};
其中步驟2中 create<sinks::basic_file_sink_mt>(logger_name, filename, truncate)
中 sinks::basic_file_sink_mt 對應詞函數模板中的模板參數Sink,入參logger_name對應此函數中的入參 std::string logger_name; 入參filename, truncate 對應此函數中的SinkArgs 可變模板參數
- 一個例子
void print()
{
std::cout << "empty" << std::endl;
}
//展開函數
template <class T, class ...Args>
void print(T head, Args... rest)
{
std::cout << "parameter " << head << std::endl;
print(rest...);
}
print(1, 2.2, 3, 4); 輸出 1 2.2 3 4 empty
print(1, 2.2, 3, 4); 同上,參數T自動推導
如果把print中調用修改爲print(rest…) 結果是什麼樣呢?這樣就類型不會自動推導,所有入參 類型爲int,輸出2.2就會變爲2
所以可變模板參數需要格外注意參數類型。
##periodic_worker
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
{
active_ = (interval > std::chrono::seconds::zero());
if (!active_)
{
return;
}
worker_thread_ = std::thread([this, callback_fun, interval]() {
for (;;)
{
std::unique_lock<std::mutex> lock(this->mutex_);
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
{
return; // active_ == false, so exit this thread
}
callback_fun();
}
});
}
~periodic_worker()
{
if (worker_thread_.joinable())
{
{
std::lock_guard<std::mutex> lock(mutex_);
active_ = false;
}
cv_.notify_one();
worker_thread_.join();
}
}
通過條件量實現timmer. 析構時重置標記位active_,然後notify.