創建Loggers
每個Logger都包含一個數組,數組里是std::shared_ptr
使用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