C++日志组件 spdlog

spdlog 是基于C++ 11的开源的轻量级的日志组件,引入也非常简单,仅仅需要引入头文件就可以了。

线程安全

命名空间 spdlog 之下的大多数函数都是线程安全的,除了:

void spdlog::set_pattern(const std::string&);
void spdlog::set_formatter(formatter_ptr);
void spdlog::set_error_handler(log_err_handler);

日志器对象的大部分方法也是线程安全的,除了:

void spdlog::logger::set_pattern(const std::string&);
void spdlog::logger::set_formatter(formatter_ptr);
void spdlog::set_error_handler(log_err_handler);

所有以 _mt 结尾的 Sink 都是用于多线程的,以 _st 结尾的则是用于单线程的,不过现在单线程的程序很少了吧,建议直接用以 _mt 结尾的多线程安全的日志对象;

代码示例

#include "spdlog/spdlog.h"
#include <iostream>
 
// 多线程的基于控制台(stdout)的日志记录器,支持高亮。类似的stdout_color_st是单线程版本
auto console = spdlog::stdout_color_mt( "console" );
// 基于文件的简单日志
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
// 基于滚动文件的日志,每个文件5MB,三个文件
auto logger = spdlog::rotating_logger_mt("file_logger", "myfilename", 1024 * 1024 * 5, 3);
 
// 定制输出格式
spdlog::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
 
// 多个日志器共享SINK
auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logfile", 23, 59);
// 下面几个同步日志器共享的输出到目标文件
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); 
 
// 一个日志器使用多个SINK
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 ));
spdlog::register_logger( combined_logger );
 
// 异步
// 每个日志器分配8192长度的队列,队列长度必须2的幂
spdlog::set_async_mode(8192); 
// 程序退出前清理
spdlog::drop_all();
 
// 注册日志器
spdlog::register_logger(net_logger);
// 注册后,其它代码可以根据名称获得日志器
auto logger = spdlog::get(net_logger);
 
// 记录日志
// 设置最低级别
console->set_level(spdlog::level::debug);
console->debug("Hello World") ;
// 使用占位符
console->info("Hello {}" ,"World"); 
// 带格式化的占位符:d整数,x十六进制,o八进制,b二进制                
console->warn("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// 带格式化的占位符:f浮点数
console->info("Support for floats {:03.2f}", 1.23456);
// 左对齐,保证30字符宽度
console->error("{:<30}", "left aligned");
// 指定占位符位置序号
console->info("Positional args are {1} {0}..", "too", "supported");
 
// 记录自定义类型,需要重载<<操作符
#include <spdlog/fmt/ostr.h> 
class Duck{}
std::ostream& operator<<(std::ostream& os, const Duck& duck){ 
    return os << duck.getName(); 
}
Duck duck;
console->info("custom class with operator<<: {}..", duck);

输出格式

spdlog默认的输出格式为:

[2018-03-01 23:46:59.678] [info] [my_loggername] Some message

要定制输出格式,可以调用:

//估计大部分人都喜欢下面这样的格式输出格式:
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%l] %v");
//或者实现自己的格式化器:
spdlog::set_formatter(std::make_shared<my_custom_formatter>());

输出格式为:

[2018-03-01 23:28:04.285][13464][info] Input host name:gobert, ptr:0x9c6a78

Pattern 格式说明

输出格式的 Pattern 中可以有若干 % 开头的标记,含义如下表:

标记 说明
%v 实际需要被日志记录的文本,如果文本中有{占位符}会被替换
%t 线程标识符
%P 进程标识符
%n 日志记录器名称
%l 日志级别
%L 日志级别简写
%a 简写的周几,例如Thu
%A 周几,例如Thursday
%b 简写的月份,例如Aug
%B 月份,例如August
%c 日期时间,例如Thu Aug 23 15:35:46 2014
%C 两位年份,例如14
%Y 四位年份,例如2014
%D 或 %x MM/DD/YY格式日期,例如”08/23/14
%m 月份,1-12之间
%d 月份中的第几天,1-31之间
%H 24小时制的小时,0-23之间
%I 12小时制的小时,1-12之间
%M 分钟,0-59
%S 秒,0-59
%e 当前秒内的毫秒,0-999
%f 当前秒内的微秒,0-999999
%F 当前秒内的纳秒, 0-999999999
%p AM或者PM
%r 12小时时间,例如02:55:02 pm
%R 等价于%H:%M,例如23:55
%T 或 %X HH:MM:SS
%z 时区UTC偏移,例如+02:00
%+ 表示默认格式

个人实战应用源码

根据个人的习惯,喜欢在开发后端程序时,喜欢将 log 同时输出到控制台和文件中,那么根据 spdlog 的接口,需要在创建对象时将 console 和 file sink 均传入即可,代码如下:

    spdlog::set_async_mode(4096);
    spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%l] %v");
    //创建控制台对象指针
    auto console_log_ptr = spdlog::stdout_logger_mt("console");
    //创建文件对象指针
    auto file_log_ptr = spdlog::rotating_logger_mt("file", "opc-collector.log", 100 * 1024, 2);
    //将以上两种日志对象 sink 组合在一起
    spdlog::sinks_init_list sink_list = { console_log_ptr->sinks().front(), file_log_ptr->sinks().front() };
    //创建一个新的日志对象,以上面两个日志对象作为初始化参数,即实现了同时输出 console 和 file
    auto log_ptr = spdlog::create("loggername", sink_list);
    
    log_ptr->info("Test spdlog output:string:{}, int:{}", "spdlog info test string", 123456);
    log_ptr->flush();

注:将多个日志对象同时输出到一个日志对象时,其 loggername 不能保持一致,否则报错警告称:loggername 重复!

以上内容大部分参考自:https://blog.gmem.cc/spdlog

版权声明: 如无特别声明,本文版权归 Mr Chen 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:《 c++ 开源日志库 spdlog 》

本文链接:https://gbcpp.github.io/cpp-log-library-spdlog.html

本文最后一次更新为 天前,文章中的某些内容可能已过时!