spdlog簡介


創建Loggers

每個Logger都包含一個數組,數組里是std::shared_ptr ,每次調用log時,logger會在每個sink上調用sink(log_msg)。sink分為多線程和單線程版本,單線程版本的sink不允許在多線程里調用

使用factory創建logger

//Create and return a shared_ptr to a multithreaded console logger.
#include "spdlog/sinks/stdout_color_sinks.h"
auto console = spdlog::stdout_color_mt("some_unique_name");

這里會創建console logger,並加入到spdlog的全局注冊器里,使用some_unique_name作為id,並返回shared_ptr

使用spdloge::get("xxx")返回logger

通過spdlog::get()方法獲取一個logger。注意這里是加鎖實現的,最好不好頻繁調用,比較好的方法是在構造時獲取。手動創建的logger不會自動注冊,需要主動調用register_logger()

spd::register_logger(my_logger)

創建rotating file logger

//Create rotating file multi-threaded logger
#include "spdlog/sinks/rotating_file_sink.h"
auto file_logger = spdlog::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
...
auto same_logger= spdlog::get("file_logger");

創建異步logger

#include "spdlog/async.h"
void async_example()
{
    // default thread pool settings can be modified *before* creating the async logger:
    // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
    // alternatively:
    // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");

}

spdlog使用共享的全局線程池和專用的消息隊列實現異步logging。 創建可變數目的預先分配slots的消息隊列,可以使用spdlog::init_thread_pool(queue_size, backing_threads_count)來修改線程數目和隊列大小。當嘗試log一條消息時,如果隊列滿了,調用者會block直到有一個slot可以使用(默認行為)。當logger構造函數傳參async_overflow_policy==overrun_oldest時,不會block,而是在隊列里使用新的日志消息覆蓋最早的日志消息

手動創建logger

auto sink = std::make_shared<spdlog::sink::stdout_sink_mt>();
auto my_logger = std::make_shared<spdlog::logger>("mylogger", sink);

使用多個sinks創建logger

std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_st>());
sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_st>("log_file", 23, 59));
auto logger = std::make_shared<spdlog::logger>("mylogger", begin(sinks), end(sinks));
spdlog::register_logger(logger);

多個文件logger共享同一個文件

auto sharedFileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("fileName.txt");
auto firstLogger = std::make_shared<spdlog::logger>("first", sharedFileSink);
auto secondLogger = std::make_unique<spdlog::logger>("second", sharedFileSink);

格式

日志格式可以使用
(1) set_pattern(pattern_string)
(2) 實現formatter的接口,然后調用set_formatter(std::make_unique ());

使用set_pattern()

  • 全局使用 spdlog::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
  • 針對某個logger some_logger->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
  • 針對某個sink
some_logger->sinks()[0]->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
some_logger->sinks()[1]->set_pattern("..");

格式flag標識

flag meaning example
%v The actual text to log "some user text"
%t Thread id "1232"
%P Process id "3456"
%n Logger's name "some logger name"
%l The log level of the message "debug", "info", etc
%L Short log level of the message "D", "I", etc
%a Abbreviated weekday name "Thu"
%A Full weekday name "Thursday"
%b Abbreviated month name "Aug"
%B Full month name "August"
%c Date and time representation "Thu Aug 23 15:35:46 2014"
%C Year in 2 digits "14"
%Y Year in 4 digits "2014"
%D or %x Short MM/DD/YY date "08/23/14"
%m Month 01-12 "11"
%d Day of month 01-31 "29"
%H Hours in 24 format 00-23 "23"
%I Hours in 12 format 01-12 "11"
%M Minutes 00-59 "59"
%S Seconds 00-59 "58"
%e Millisecond part of the current second 000-999 "678"
%f Microsecond part of the current second 000000-999999 "056789"
%F Nanosecond part of the current second 000000000-999999999 "256789123"
%p AM/PM "AM"
%r 12 hour clock "02:55:02 pm"
%R 24-hour HH:MM time, equivalent to %H:%M "23:55"
%T or %X ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S "23:55:59"
%z ISO 8601 offset from UTC in timezone ([+/-]HH:MM) "+02:00"
%E Seconds since the epoch "1528834770"
%% The % sign "%"
%+ spdlog's default format "[2014-10-31 23:46:59.678] [mylogger] [info] Some message"
%^ start color range (can be used only once) "[mylogger] [info(green)] Some message"
%$ end color range (for example %^[+++]%$ %v) (can be used only once) [+++] Some message
%@ Source file and line (use SPDLOG_TRACE(..), SPDLOG_INFO(...) etc. instead of spdlog::trace(...) my_file.cpp:123
%s Basename of the source file (use SPDLOG_TRACE(..), SPDLOG_INFO(...) etc.) my_file.cpp
%g Full or relative path of the source file as appears in the __FILE__ macro (use SPDLOG_TRACE(..), SPDLOG_INFO(...) etc.) /some/dir/my_file.cpp
%# Source line (use SPDLOG_TRACE(..), SPDLOG_INFO(...) etc.) 123
%! Source function (use SPDLOG_TRACE(..), SPDLOG_INFO(...) etc. see tweakme for pretty-print) my_func
%o Elapsed time in milliseconds since previous message 456
%i Elapsed time in microseconds since previous message 456
%u Elapsed time in nanoseconds since previous message 11456
%O Elapsed time in seconds since previous message 4

sinks

  • sink是實際寫入日志的對象,每個sink應該只有一個日志輸出地,每一個sink也有自己的formatter對象。
  • 每個logger有多個std::shared_ptr ,每次調用log時(如果日志級別正確),logger會調用sink(log_msg)方法
  • 分為單線程和多線程的sinks

常用的sink

  • rotating_file_sink:當文件達到最大容量時自動創建新的日志文件
  • daily_file_sink:內天都創建新的日志文件,可以指定時間
  • simple_file_sink:簡單的日志文件,沒有任何限制
  • stdout_sink/stderr_sink: 輸出到控制台的sink
  • ostream_sink:寫入到std::ostringstream
  • null_sink:不會記錄日志,直接刪除
  • syslog_sink:發送日志到syslog
  • systemd_sink:發送日志到systemd
  • dist_sink:分布式日志到所有的其他sink上
  • msvc_sink:windows debug sink
  • dup_filter_sink:移除重復的message的sink

實現自己的sink

  • 繼承bask_sink
  • 實現sink_it_()和flush()
#include "spdlog/sinks/base_sink.h"

template<typename Mutex>
class my_sink : public spdlog::sinks::base_sink <Mutex>
{
...
protected:
    void sink_it_(const spdlog::details::log_msg& msg) override
    {

    // log_msg is a struct containing the log entry info like level, timestamp, thread id etc.
    // msg.raw contains pre formatted log

    // If needed (very likely but not mandatory), the sink formats the message before sending it to its final destination:
    spdlog::memory_buf_t formatted;
    spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
    std::cout << fmt::to_string(formatted);
    }

    void flush_() override 
    {
       std::cout << std::flush;
    }
};

#include "spdlog/details/null_mutex.h"
#include <mutex>
using my_sink_mt = my_sink<std::mutex>;
using my_sink_st = my_sink<spdlog::details::null_mutex>;

在創建logger后添加sink

通過獲取sinks的引用,然后添加sink

inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
{
    return sinks_;
}


spdlog::get("myExistingLogger")->sinks().push_back(myNewSink);

logger的注冊器

spdlog使用全局的注冊器保存所有的logger.這是為了能夠在工程的任何地方找到此log,而不需要傳遞這個logger

spdlog::get("logger1")->info("hello");
.. 
.. 
some other source file..
..
auto l = spdlog::get("logger1");
l->info("hello again");

注冊新的logger

正常情況下回自動注冊,為了注冊手動創建的logger,需要調用spdlog::register_logger(some_logger);

注冊沖突

如果存在相同名字的logger,會拋出異常

移除logger

使用drop()移除

spdlog::drop("logger_name");
//or remove them all
spdlog::drop_all()

創建異步日志

創建異步日志方法

(1)spdlog::async_factory模版參數

#include "spdlog/async.h"
void async_example()
{
    // default thread pool settings can be modified *before* creating the async logger:
    // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
}

(2) 使用spdlog::create_async

auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");    

(3)使用spdlog::create_async_nb

auto async_file = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");

(4)直接創建並使用全局線程池

spdlog::init_thread_pool(queue_size, n_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, spdlog::thread_pool(), async_overflow_policy::block);

(5)直接創建並使用自定義的線程池

auto tp = std::make_shared<details::thread_pool>(queue_size, n_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, tp, async_overflow_policy::block);

隊列滿后策略

  • block調用者知道有足夠空間
  • 覆蓋隊列里最舊的日志。需要使用create_async_nb模版參數或者構造logger時設置使用spdlog::async_overflow_policy
auto logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
// or directly:
 auto logger = std::make_shared<async_logger>("as", test_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::overrun_oldest);

線程池

默認會創建全局的線程池,queue size為8192,1個工作線程用於服務所有的異步logger。這意味着創建和銷毀異步日志器是很輕量級的,因為他們不會持有或創建任何后台線程或者隊列,而是由共享的線程池對象。所有隊列是提前申請好空間的,可以reset

spdlog::init_thread_pool(queue_size, n_threads);

注意到這會銷毀原來的全局線程池而創建新的線程池,使用舊的線程池的logger將無法工作,因此建議在創建異步日志器之前調用

如果不同的日志器需要不同的隊列,可以創建不同的線程池

auto tp = std::make_shared<details::thread_pool>(128, 1);
auto logger = std::make_shared<async_logger>("as", some_sink, tp, async_overflow_policy::overrun_oldest);

auto tp2 = std::make_shared<details::thread_pool>(1024, 4);  // create pool with queue of 1024 slots and 4 backing threads
auto logger2 = std::make_shared<async_logger>("as2", some_sink, tp2, async_overflow_policy::block);

日志消息順序

使用單個work線程的線程池可以保證順序,如果是多個work線程,無法保證日志順序

windows問題

當在windows平台使用異步日志時,應該在main函數退出時調用spdlog::shutdown()

flush策略

為了較好的性能,默認使用libc的BUFSIZ,可以使自定義的flush策略

  • 手動flush。
    當調用logger->flush()時,會立刻flushlogger的日志
  • 設置日志級別觸發flush
my_logger->flush_on(spdlog::level::err); 
  • 指定時間間隔flush
spdlog::flush_every(std::chrono::seconds(5));

默認的logger

  • spdlog創建了默認的全局logger(stdout colored multithread)
  • 使用方法
spdlog::set_default_logger(some_other_logger);
spdlog::info("Use the new default logger");

其他

日志文件一直為空

為了性能,log不會立即flush到文件里,等到BUFSIZ bytes的日志寫到log里才一次性寫入
為了能夠強制flush,可以使用以下方法

my_logger->flush();                            // flush now
my_logger->flush_on(spdlog::level::info);      // auto flush when "info" or higher message is logged
spdlog::flush_on(spdlog::level::info);         // auto flush when "info" or higher message is logged on all loggers
spdlog::flush_every(std::chrono::seconds(5));  // flush periodically every 5 seconds (caution: must be _mt logger)

SPDLOG_ACTIVE_LEVEL

通過在包含spdlog.h之前定義SPDLOG_ACTIVE_LEVEL可以移除所有編譯時的debug語句

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO // All DEBUG/TRACE statements will be removed by the pre-processor
#include "spdlog/spdlog.h"
...
SPDLOG_DEBUG("debug message to default logger"); // removed at compile time
SPDLOG_LOGGER_TRACE(my_logger, "trace message"); // removed at compile time
SPDLOG_INFO("info message to default logger");   // included

在windows系統退出異步日志

在main函數的結尾處調用

spdlog::shutdown();

線程安全

不是線程安全的函數

  • set_error_handler(log_err_handle)
  • logger::sinks() 返回vector的引用,不能並發修改

Loggers

_mt后綴是線程安全的版本: auto logger = spdlog::basic_logger_mt(...);
_st是單線程的logger: auto logger = spdlog::basic_logger_st(...)

sinks

  • _mt結尾的是線程安全的:daily_file_sink_mt
  • _st結尾的是非線程安全的:daily_file_sink_st


免責聲明!

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



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