spdlog 文档
创建Loggers
每个Logger都包含一个数组,数组里是std::shared_ptr<:sink>,每次调用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