转自:spdlog学习笔记
说明:所有内容翻译自spdlog的wiki,受英语水平所限,有所错误或失真在所难免,如果您有更好的建议,请在博文下留言。
线程安全
spdlog::
命名空间下的是线程安全的,当loggers在不同的线程同时执行时,下述函数不应该被调用:
spdlog::set_error_handler(log_err_handler); // or logger->set_error_handler(log_err_handler);
logger在其它线程执行过程中,添加或移除sink
是线程不安全的
logger->sinks().push_back(new_sink); // Don't do this if other thread is already using this logger
要创建线程安全的loggers,使用带 _mt
后缀的工厂函数,例如:
auto logger = spdlog::basic_logger_mt(...);
要创建单线程的loggers,使用带 _st
后缀的工厂函数,例如:
auto logger = spdlog::basic_logger_st(...);
对于sinks
,以 _mt
后缀结尾的是线程安全的,比如:daily_file_sink_mt
以_st
后缀结尾的是非线程安全的,比如:daily_file_sink_st
快速开始
#include "spdlog/spdlog.h"
int main()
{
//Use the default logger (stdout, multi-threaded, colored)
spdlog::info("Hello, {}!", "World");
}
- 1
- 2
- 3
- 4
- 5
spdlog
是个只有头文件的库,只需要将头文件拷贝到你的工程就可以使用了,编译器需要支持C++11
它使用一个类似python的格式API库fmt:
logger->info("Hello {} {} !!", "param1", 123.4);
spdlog
支持使用最小集的方式,意味着你只用包含你实际需要的头文件,而不是全部,比如说你只需要使用 rotating logger
,那么你只需要
#include <spdlog/sinks/rotating_file_sink.h>
对于异步特性,你还需要
#include <spdlog/asynch.h>
基本用法示例
#include <iostream>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h" // support for basic file logging
#include "spdlog/sinks/rotating_file_sink.h" // support for rotating file logging
int main(int, char* [])
{
try
{
// Create basic file logger (not rotated)
auto my_logger = spdlog::basic_logger_mt(“basic_logger”, “logs/basic.txt”);
<span class="token comment">// create a file rotating logger with 5mb size max and 3 rotated files</span>
<span class="token keyword">auto</span> file_logger <span class="token operator">=</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">rotating_logger_mt</span><span class="token punctuation">(</span><span class="token string">"file_logger"</span><span class="token punctuation">,</span> <span class="token string">"myfilename"</span><span class="token punctuation">,</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&</span> ex<span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator"><<</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator"><<</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><<</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
使用工厂函数创建异步logger:
#include "spdlog/async.h" //support for async logging.
#include "spdlog/sinks/basic_file_sink.h"
int main(int, char* [])
{
try
{
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(“async_file_logger”, “logs/async_log.txt”);
for (int i = 1; i < 101; ++i)
{
async_file->info(“Async message #{}”, i);
}
// Under VisualStudio, this must be called before main finishes to workaround a known VS issue
spdlog::drop_all();
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << “Log initialization failed: “ << ex.what() << std::endl;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
创建异步logger并改变线程池设置
#include "spdlog/async.h" //support for async logging.
#include "spdlog/sinks/basic_file_sink.h"
int main(int, char* [])
{
try
{
auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logfile", 23, 59);
// default thread pool settings can be modified *before* creating the async logger:
spdlog::init_thread_pool(10000, 1); // queue with 10K items and 1 backing thread.
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
spdlog::drop_all();
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
创建一个由多个loggers共享同一个输出文件的sink
#include <iostream> #include "spdlog/spdlog.h" #include "spdlog/sinks/daily_file_sink.h" int main(int, char* []) { try { auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logfile", 23, 59); // create synchronous loggers auto net_logger = std::make_shared<spdlog::logger>("net", daily_sink); auto hw_logger = std::make_shared<spdlog::logger>("hw", daily_sink); auto db_logger = std::make_shared<spdlog::logger>("db", daily_sink);
net_logger<span class="token operator">-></span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>critical<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// independent levels</span> hw_logger<span class="token operator">-></span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>debug<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// globally register the loggers so so the can be accessed using spdlog::get(logger_name)</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">register_logger</span><span class="token punctuation">(</span>net_logger<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator"><<</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator"><<</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><<</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
创建一个对应多个sink
的logger,每一个sink
都有独有的格式和日志级别
// // Logger with console and file output. // the console will show only warnings or worse, while the file will log all messages. // #include <iostream> #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" // or "../stdout_sinks.h" if no colors needed #include "spdlog/sinks/basic_file_sink.h" int main(int, char* []) { try { auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level(spdlog::level::warn); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
<span class="token keyword">auto</span> file_sink <span class="token operator">=</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>make_shared<span class="token operator"><</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>sinks<span class="token punctuation">:</span><span class="token punctuation">:</span>basic_file_sink_mt<span class="token operator">></span><span class="token punctuation">(</span><span class="token string">"logs/multisink.txt"</span><span class="token punctuation">,</span> true<span class="token punctuation">)</span><span class="token punctuation">;</span> file_sink<span class="token operator">-></span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>trace<span class="token punctuation">)</span><span class="token punctuation">;</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>logger <span class="token function">logger</span><span class="token punctuation">(</span><span class="token string">"multi_sink"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>console_sink<span class="token punctuation">,</span> file_sink<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>debug<span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"this should appear in both console and file"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> logger<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"this message should not appear in the console, only in the file"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator"><<</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator"><<</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><<</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
日志宏定义
在包含 *spdlog.h”之前,添加 SPDLOG_ACTIVE_LEVEL
宏定义可以设置期望的日志级别
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
SPDLOG_LOGGER_TRACE(file_logger , "Some trace message that will not be evaluated.{} ,{}", 1, 3.23);
SPDLOG_LOGGER_DEBUG(file_logger , "Some Debug message that will be evaluated.. {} ,{}", 1, 3.23);
SPDLOG_DEBUG("Some debug message to default logger that will be evaluated");
- 1
- 2
- 3
记录用户定义的对象
#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h" // must be included
#include "spdlog/sinks/stdout_sinks.h"
class some_class { };
std::ostream& operator<<(std::ostream& os, const some_class& c)
{
return os << “some_class”;
}
void custom_class_example()
{
some_class c;
auto console = spdlog::stdout_logger_mt(“console”);
console->info(“custom class with operator<<: {}…”, c);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
创建 logger
每一个logger中包含一个存有一个或多个 std::shared_ptr<spdlog::sink>
的 vector
logger在记录每一条日志时(如果是有效的级别),将会调用每一个std::shared_ptr<spdlog::sink>
中的sink(log_msg)
函数
spdlog用_mt
(多线程)和_st
(单线程)后缀标识sink
是否是线程安全
单线程的sink
不可以在多线程中使用,它的速度会更快,因为没有锁竞争
使用工厂函数创建loggers
//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");
- 1
- 2
这样就创建了一个console logger,并以”some_unique_name”作为它的id,将自身注册到spdlog
,返回自身的智能指针
使用spdlog::get("...")
访问loggers
loggers可以在任何地方使用线程安全的spdlog::get("logger_name")
来进行访问,返回智能指针
注意:spdlog::get
可能会拖慢你的程序,因为它内部维护了一把锁,所以要谨慎使用。比较推荐的用法是保存返回的shared_ptr<spdlog::logger>
,直接使用它,至少在频繁访问的代码中。
一个很好的方法是建立一个std::shared_ptr<spdlog::logger>
私有成员变量,并在构造函数中初始化:
class MyClass
{
private:
std::shared_ptr<spdlog::logger> _logger;
public:
MyClass()
{
//set _logger to some existing logger
_logger = spdlog::get("some_logger");
//or create directly
//_logger = spdlog::rotating_file_logger_mt("my_logger", ...);
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
注意2:手动创建的logger不会被自动注册,并且也不会通过get(...)
调用查找到
注册手动创建的logger对象需要使用register_logger(...)
函数:
spdlog::register_logger(my_logger);
...
auto the_same_logger = spdlog::get("mylogger");
- 1
- 2
创建 rotating file logger
//Create rotating file multi-threaded logger
#include "spdlog/sinks/rotating_file_sink.h"
auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
...
auto same_logger= spdlog::get("file_logger");
- 1
- 2
- 3
- 4
创建异步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");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
对于异步日志记录,spdlog使用具有专用消息队列的共享全局线程池。
为此,它在消息队列中创建固定数量的预分配的插槽(64位中每个插槽大约256个字节),并且可以使用spdlog::init_thread_pool(queue_size,backing_threads_count)
进行修改。
当尝试记录一条日志时,并且队列已满,那么调用默认会被阻塞,并且默认直到一个插槽可用时,或者立即移除队列中最旧的日志信息,并追加最新的日志信息(如果logger以async_overflow_policy==overrun_oldest
构造)
手动创建loggers
auto sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
auto my_logger= std::make_shared<spdlog::logger>("mylogger", sink);
- 1
创建拥有多个sink
的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>("logfile", 23, 59));
auto combined_logger = std::make_shared<spdlog::logger>("name", begin(sinks), end(sinks));
//register it if you need to access it globally
spdlog::register_logger(combined_logger);
- 1
- 2
- 3
- 4
- 5
创建多个具有同一个输出文件的file logger
auto sharedFileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("fileName.txt");
auto firstLogger = std::make_shared<spdlog::logger>("firstLoggerName", sharedFileSink);
auto secondLogger = std::make_unique<spdlog::logger>("secondLoggerName", sharedFileSink);
- 1
- 2
自定义格式
每一个logger的sink
都有一个格式化器,用来格式化消息为目标格式
spdlog
默认的日志格式为:
[2019-04-18 13:31:59.678] [info] [my_loggername] Some message
- 1
有两种方式可以自定义logger的格式:
- 设置模式字符串(推荐)
set_pattern(pattern_string);
- 或者实现自定义格式器,实现
formatter
接口,并调用
set_formatter(std::make_shared<my_custom_formatter>());
使用“`set_pattern(…)`自定义格式
格式应用到所有被注册的logger:
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("..");
- 1
性能
无论何时用户调用set_pattern(...)
,库都会将新模式设置为内部有效标识 – 这种方式使得即便在复杂模式下,性能仍然很高(每次记录日志时不会重新解析模式)
模式标记
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 1-12 | “11” |
%d |
Day of month 1-31 | “29” |
%H |
Hours in 24 format 0-23 | “23” |
%I |
Hours in 12 format 1-12 | “11” |
%M |
Minutes 0-59 | “59” |
%S |
Seconds 0-59 | “58” |
%e |
Millisecond part of the current second 0-999 | “678” |
%f |
Microsecond part of the current second 0-999999 | “056789” |
%F |
Nanosecond part of the current second 0-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” |
%i |
Message sequence number (disabled by default – edit ‘tweakme.h’ to enable) | “1154” |
%% |
The % sign | “%” |
%+ |
spdlog’s default format | “[2014-10-31 23:46:59.678] [mylogger] [info] Some message” |
%^ |
start color range | “[mylogger] [info(green)] Some message” |
%$ |
end color range (for example %1%$ %v) | [+++] Some message |
%@ |
Source file and line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) | my_file.cpp:123 |
%s |
Source file (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) | 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 |
对齐
每一个模式标记可以通过预先添加一个宽度标记来对齐(上限128)
使用-
(左对齐)或者=
(中间对齐)去控制向哪边对齐:
align | meaning | example | result |
---|---|---|---|
%<width><flag> |
Align to the right | %8l |
” info” |
%-<width><flag> |
Align to the left | %-8l |
“info “ |
%=<width><flag> |
Align to the center | %=8l |
” info “ |
sink
sink
是实际将日志写入目标位置的对象。每一个sink
仅应负责写一个目标文件(比如 file,console,db),并且每一个sink
有专属的私有格式化器formatter
实例。
可用的sink
rotating_file_sink
达到最大文件大小时,关闭文件,重命名文件并创建新文件。 最大文件大小和最大文件数都可以在构造函数中配置。
注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹
// create a thread safe sink which will keep its file size to a maximum of 5MB and a maximum of 3 rotated files.
#include "spdlog/sinks/rotating_file_sink.h"
...
auto file_logger = spdlog::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
- 1
- 2
- 3
或者手动创建sink
并将它传递给logger:
#include "spdlog/sinks/rotating_file_sink.h"
...
auto rotating = make_shared<spdlog::sinks::rotating_file_sink_mt> ("log_filename", "log", 1024*1024, 5, false);
auto file_logger = make_shared<spdlog::logger>("my_logger", rotating);
- 1
- 2
- 3
daily_file_sink
每天在一个特别的时间创建一个新的日志文件,并在文件名字上添加一个时间戳
注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹
#include "spdlog/sinks/daily_file_sink.h"
..
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily", 14, 55);
- 1
- 2
将会在每天的14:55创建一个新的日志文件,并创建一个线程安全的sink
simple_file_sink
无任何限制的向一个日志文件中写入
注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹
#include "spdlog/sinks/basic_file_sink.h"
...
auto logger = spdlog::basic_logger_mt("mylogger", "log.txt");
- 1
- 2
stdout_sink/stderr_sink with colors
#include "spdlog/sinks/stdout_color_sinks.h"
...
auto console = spdlog::stdout_color_mt("console");
auto err_console = spdlog::color_logger_mt("console");
- 1
- 2
- 3
或者直接创建sink
:
auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
ostream_sink
#include "spdlog/sinks/ostream_sink.h "
...
std::ostringstream oss;
auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_mt> (oss);
auto logger = std::make_shared<spdlog::logger>("my_logger", ostream_sink);
- 1
- 2
- 3
- 4
null_sink
会丢弃所有到它的日志
#include "spdlog/sinks/null_sink.h"
...
auto logger = spdlog::create<spdlog::sinks::null_sink_st>("null_logger");
- 1
- 2
syslog_sink
POSIX syslog(3) 发送日志到syslog
#include "spdlog/sinks/syslog_sink.h"
...
auto syslog_logger = spdlog::syslog_logger("syslog", "my_ident");
- 1
- 2
dist_sink
将日志消息分发到其他接收器列表
#include "spdlog/sinks/syslog_sink.h"
...
auto dist_sink = make_shared<spdlog::sinks::dist_sink_st>();
auto sink1 = make_shared<spdlog::sinks::stdout_sink_st>();
auto sink2 = make_shared<spdlog::sinks::simple_file_sink_st>("mylog.log");
dist_sink->add_sink(sink1);
dist_sink->add_sink(sink2);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
msvc_sink
Windows debug sink (使用OutputDebugStringA窗口输出日志)
#include "spdlog/sinks/msvc_sink.h"
auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
auto logger = std::make_shared<spdlog::logger>("msvc_logger", sink);
- 1
- 2
实现自己的sink
实现自己的sink
,你需要实现sink
类的接口
一种推荐的方式是继承自base_sink
类
该类已经处理了线程锁,使得实现一个线程安全的sink
非常容易
#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
{
<span class="token comment">// log_msg is a struct containing the log entry info like level, timestamp, thread id etc.</span>
<span class="token comment">// msg.raw contains pre formatted log</span>
<span class="token comment">// If needed (very likely but not mandatory), the sink formats the message before sending it to its final destination:</span>
fmt<span class="token punctuation">:</span><span class="token punctuation">:</span>memory_buffer formatted<span class="token punctuation">;</span>
sink<span class="token punctuation">:</span><span class="token punctuation">:</span>formatter_<span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span>msg<span class="token punctuation">,</span> formatted<span class="token punctuation">)</span><span class="token punctuation">;</span>
std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator"><<</span> fmt<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">to_string</span><span class="token punctuation">(</span>formatted<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">flush_</span><span class="token punctuation">(</span><span class="token punctuation">)</span> override
<span class="token punctuation">{<!-- --></span>
std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator"><<</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>flush<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
};
#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>;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
创建sink
后,将其添加至logger
由于在spdlog v1.x 版本中有一个函数返回一个非常引用的skins vector,它允许你手动的向skins中添加。对于这个skins vector没有锁保护,因此它不是线程安全的。
inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
{
return sinks_;
}
- 1
- 2
- 3
logger及注册
spdlog包含一个进程内全局的注册机制,对于本进程内所有创建的loggers
目的是在项目的任何地方轻松访问loggers,而不会传递它们。
spdlog::get("logger1")->info("hello");
..
..
some other source file..
..
auto l = spdlog::get("logger1");
l->info("hello again");
- 1
- 2
- 3
- 4
- 5
- 6
如果未找到logger,会返回一个空智能指针。你应该判断智能指针的有效性
注册新的loggers
一般情况下没必要去注册loggers,因为它们已经自动注册了
手动创建的loggers,需要自己去注册,使用 register_logger(std::shared_ptr<logger>)
函数:
spdlog::register_logger(some_logger);
将使用some_logger的name来注册它自己
注册冲突
当尝试注册一个名字已经被注册过的logger时,spdlog会抛出一个 spdlog::spdlog_ex
异常
从注册器中移除loggers
drop()
函数可以用来从注册器中移除一个logger
如果logger智能指针没有其它引用时,该logger将会被关闭并且释放它相关的资源
spdlog::drop("logger_name");
//or remove them all
spdlog::drop_all()
- 1
- 2
异步日志
创建异步logger有多种方式。你需要 #include "spdlog/async.h
使用 <spdlog::async_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");
}
- 1
- 2
- 3
- 4
- 5
- 6
使用 spdlog::create_async<sink>
auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
使用 spdlog::create_async_nb<sink>
创建即便队列满也永远不会阻塞的logger
auto async_file = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
直接构造,并使用全局线程池
auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, spdlog::thread_pool(), async_overflow_policy::block);
直接构造,使用自定义线程池
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);
- 1
直接构造,使用自定义线程池
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);
- 1
注意:上例中的tp对象的生命期一定要长于logger对象,因为logger需要一个tp对象的weak_ptr
队列满时的决策
当队列满了的时候有两种可选方式:
- 阻塞调用直到有空间可用(默认行为)
- 移除并替换队列中最旧的信息,不用等待可用空间
使用 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);
- 1
- 2
spdlog的线程池
默认情况下,spdlog创建一个全局的线程池,队列大小为8192,一个工作线程服务于所有的async loggers
这意味着创建和销毁 async loggers
非常廉价,因为它们不拥有或者创建任何后台线程或队列–它们被共享的线程池对象创建和管理
队列中所有的槽都是在线程池构造时预分配的(64位系统中每个槽占256字节)
线程池的大小和线程可以被重置:
spdlog::init_thread_pool(queue_size, n_threads);
注意:这将会销毁之前的全局线程池对象tp,并创建一个新的线程池–这也意味着所有使用旧的线程池tp的loggers都将停止工作,因此建议在任何async loggers
被创建之前调用该函数
如果不同的loggers必须要使用不同的队列,那么可以创建不同的线程池,并传递给loggers:
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);
- 1
- 2
- 3
- 4
Windows上的问题
在VS运行时存在一个bug,在退出时会导致应用程序死锁。如果你使用异步日志记录,一定要确保在main()函数退出时调用spdlog::shutdown()
函数
Flush策略
默认情况下,spdlog允许底层libc在它认为合适时进行刷新,以获得良好的性能。 您可以使用以下选项覆盖它:
手动flush
使用logger->flush()
函数让logger去flush它的内容,logger将依次调用包含的每一个sink
上的flush()
函数
注意:如果使用async logger
,logger->flush()
会发送一个消息到队列,请求flush操作,因此函数会立即返回。这跟一些旧版本的spdlog是有区别的(老版本会同步等待直到flush完成,并接收到消息)
基于严重性的flush
可以设置一个最小日志等级来触发自动flush
下例中,只要error或者更严重的日志被记录时就会触发flush:
my_logger->flush_on(spdlog::level::err);
基于间隔的flush
spdlog支持设置flush间隔。由一个单独的工作线程定期调用每一个logger的flush()实现
下例中,对所有已注册的loggers定期5秒调用flush():
spdlog::flush_every(std::chrono::seconds(5));
注意:仅应在线程安全的loggers上使用该特性,因为定期flush是从不同的线程发生的
默认的logger
为方便起见,spdlog创建了一个默认的全局记录器(stdout,colors和multithreaded)。
通过直接调用 spdlog::info(..), spdlog::debug(..), etc
下例可以替换任何其它logger为默认logger:
spdlog::set_default_logger(some_other_logger);
spdlog::info("Use the new default logger");
- 1
错误处理
spdlog在记录过程中不会抛出异常
在构造logger或sink时可能会抛出异常,因为它认为出了严重错误
如果在日志记录过程中发生了错误,spdlog会打印错误信息到stderr
为了避免满屏幕大量打印错误信息,限制速率为每个logger 1 条消息/分钟
该行为可以被改变,通过调用spdlog::set_error_handler(new_handler_fun)
或者 logger->set_error_handler(new_handler_fun)
修改全局错误处理句柄
spdlog::set_error_handler([](const std::string& msg) {
std::cerr << "my err handler: " << msg << std::endl;
});
- 1
- 2
对于特定的logger
critical_logger->set_error_handler([](const std::string& msg) {
throw std::runtime_error(msg);
});
- 1
- 2
默认错误处理句柄
default_err_handler
会使用下面语法打印错误
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
如何在dll中使用spdlog
由于spdlog是仅有头文件的库,构建共享库和在主程序中使用它将不会在它们之间共享注册器信息
就是说调用类似于 spdlog::set_level(spdlog::level::level_enum::info)
将不会改变dll中的loggers
解决办法
在主程序和dll中都注册logger
/* * Disclaimer: * This was not compiled but extracted from documentation and some code. */
// mylibrary.h
// In library, we skip the symbol exporting part
#include <memory>
#include <vector>
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
namespace library
{
static const std::string logger_name = “example”;
std::shared_ptr<spdlog::logger> setup_logger(std::vector<spdlog::sink_ptr> sinks)
{
auto logger = spdlog::get(logger_name);
if(not logger)
{
if(sinks.size() > 0)
{
logger = std::make_shared<spdlog::logger>(logger_name,
std::begin(sinks),
std::end(sinks));
spdlog::register_logger(logger);
}
else
{
logger = spdlog::stdout_color_mt(logger_name);
}
}
<span class="token keyword">return</span> logger<span class="token punctuation">;</span>
}
void test(std::string message)
{
auto logger = spdlog::get(logger_name);
if(logger)
{
logger->debug(“{}::{}”, FUNCTION, message);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
// In the main program
#include <mylibrary.h>
#include <spdlog/logger.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
int main()
{
// We assume that we load the library here
...
// Let's use the library
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>("logfile", 23, 59));
auto logger = library::setup_logger(sinks);
spdlog::set_level(spdlog::level::level_enum::debug); // No effect for the library.
library::test("Hello World!"); // No logging
spdlog::register_logger(logger);
// Now this will also affect the library logger
spdlog::set_level(spdlog::level::level_enum::debug);
library::test("Hello World!"); // Hurray !
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
-
+++ ↩︎
今天的文章spdlog用法分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/5528.html