spdlog

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

spdlog

上一篇:【橙子日记20】从零开始的毕业设计


下一篇:Selenium 中文API