spdlog指南
spdlog指南
spdlog 是一个只有头文件的C++日志库,速度非常快,扩展性很强,更重要的是 社区活跃,文档齐全。
spdlog
1. 快速入门
1.1. 线程安全
若要创建线程安全记录器,请使用 _mt
工厂函数。
1
auto logger = spdlog::basic_logger_mt(...);
若要创建单线程记录器,请使用 _st
工厂函数。
1
auto logger = spdlog::basic_logger_st(...);
2. 创建记录器
每个记录器包含一个或多个 std::shared_ptr<spdlog::sink>
,内部以 vector
保存。
2.1. 创建终端输出
1
2
#include "spdlog/sinks/stdout_color_sinks.h"
auto console = spdlog::stdout_logger_st("console");
2.2. 创建终端输出(默认,有颜色)
1
2
3
4
5
6
#include "spdlog/sinks/stdout_color_sinks.h"
auto console = spdlog::stdout_color_st("console");
// 或者使用静态函数
#include "spdlog/spdlog.h"
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
2.3. 创建普通文件
1
2
3
#include "spdlog/sinks/basic_file_sink.h"
// Create basic file logger (not rotated).
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true);
2.4. 创建滚动文件
1
2
#include "spdlog/sinks/rotating_file_sink.h"
auto file_logger = spdlog::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
2.5. 创建每日文件
1
2
3
#include "spdlog/sinks/daily_file_sink.h"
// Create a daily logger - a new file is created every day on 2:30am.
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
2.6. 异步创建
1
2
3
4
#include "spdlog/async.h"
// Default thread pool settings can be modified *before* creating the async logger:
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
2.7. 记录器管理
details::registry
管理所有的日志对象。使用 <name, logger>
将日志对象和其名称对应起来,后面使用的时候可以直接通过名称获取对应的日志对象。
1
2
3
4
5
6
7
8
// 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);
file_logger->info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
// ...
auto same_logger= spdlog::get("file_logger");
same_logger->info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
3. 自定义格式
3.1. 默认格式
每个记录器的接收器都有一个格式化程序,用于将消息格式化为其目标。
SPDLOG
的默认日志记录格式为:
1
[2014-10-31 23:46:59.678] [my_loggername] [info] Some message
有两种方法可以自定义记录器的格式:
- 设置模式字符串(推荐):
1
set_pattern(pattern_string);
3.2. 自定义格式使用
格式可以全局应用于所有已注册的记录器:
1
spdlog::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
或特定记录器对象:
1
some_logger->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
或特定接收器对象:
1
2
some_logger->sinks()[0]->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
some_logger->sinks()[1]->set_pattern("..");
3.3. 匹配模式
模式标志的形式类似于 strftime
函数:%flag
标志 | 意义 | 示例 |
---|---|---|
%v | 要记录的实际文本 | “some user text” |
%t | 线程标识 | “1232” |
%P | 进程标识 | “3456” |
%n | 记录器的名称 | “some logger name” |
%l | 消息的日志级别 | “debug”, “info”, etc |
%L | 消息的短日志级别 | “D”, “I”, etc |
%a | 工作日名称的缩写 | “Thu” |
%A | 工作日全名 | “Thursday” |
%b | 缩写月份名称 | “Aug” |
%B | 全月名称 | “August” |
%c | 日期和时间表示形式 | “Thu Aug 23 15:35:46 2014” |
%C | 年份 2 位数 | “14” |
%Y | 年份(4 位数字) | “2014” |
%D 或%x | 短月/日/年日期 | “08/23/14” |
%m | 01-12月 | “11” |
%d | 月中的某一天 01-31 | “29” |
%H | 小时数 24 格式 00-23 | “23” |
%I | 小时 12 格式 01-12 | “11” |
%M | 分钟 00-59 | “59” |
%S | 秒 00-59 | “58” |
%e | 毫秒部分的当前秒 000-999 | “678” |
%f | 微秒部分当前第二 000000-999999 | “056789” |
%F | 纳秒部分当前第二 000000000-999999999 | “256789123” |
%p | 上午/下午 | “AM” |
%r | 12小时制 | “02:55:02 PM” |
%R | 24 小时 HH:MM 时间,相当于 %H:%M | “23:55” |
%T 或%X | ISO 8601 时间格式 (HH:MM:SS),相当于 %H:%M:%S | “23:55:59” |
%z | ISO 8601 与 UTC 时区的偏移量 (+/-]HH:MM) | “+02:00” |
%E | 自纪元以来的秒数 | “1528834770” |
%% | % 符号 | ”%” |
%+ | SPDLOG 的默认格式 | “[2014-10-31 23:46:59.678] [mylogger] [info] Some message” |
%^ | 起始颜色范围(只能使用一次) | “[mylogger] [info(green)] Some message” |
%$ | 结束颜色范围(例如 %^[+++]%$ %v)(只能使用一次) | [+++] Some message |
%@ | 源文件和行 | some/dir/my_file.cpp:123 |
%s | 源文件的基本名称 | my_file.cpp |
%g | 宏中显示的源文件的完整或相对路径 | some/dir/my_file.cpp |
%# | 源行 | 123 |
%! | 源函数 | my_func |
%o | 自上一条消息以来经过的时间(以毫秒为单位) | 456 |
%i | 自上一条消息以来经过的时间(以微秒为单位) | 456 |
%u | 自上一条消息以来经过的时间(以纳秒为单位) | 11456 |
%O | 自上一条消息以来经过的时间(以秒为单位) | 4 |
4. 项目分析
4.1. 代码结构
spdlog
的代码结构如下:
1
2
3
4
5
6
7
8
9
10
spdlog
├─example 用法代码
├─include 实现目录
│ └─spdlog
│ ├─details 功能函数目录
│ ├─fmt {fmt} 库目录
│ ├─sinks 落地文件格式实现
│ └─*.h 异步模式,日志库接口等实现
├─src .cpp 文件,组成编译模块生成静态库使用
├─test 测试代码
- 提供的日志格式非常丰富,并且允许用户自定义需要的格式。
- 对日志文件的类型也做了充分扩展,支持控制台,普通文件,按大小滚动文件,按时间滚动文件,如果不能满足需要,可以自己扩展格式,见
spdlog/sinks/base_sink.h
。 - 支持单/多线程,异步/同步,阻塞非阻塞模式。
4.2. 逻辑图
有几个比较重要的文件:
spdlog/spdlog.h
为日志库接口,提供日志宏的属性控制函数。spdlog/logger.h
为日志管理器,为前后端连接的枢纽。spdlog/async.h
为异步模式接口。spdlog/sinks/base_sink.h
为日志文件格式父类,后面所有的日志文件格式都是继承该类来实现不同功能。spdlog/details/registry.h
用于登记所有的logger
。
4.3. 接口
4.3.1. 日志接口
spdlog
总体而言提供了日志接口:
spdlog::debug()
, 默认的日志对象,使用默认的日志信息格式,输出至stdout
。logger->debug()
, 指定日志对象进行日志记录,输出至该日志对象对应的文件中。SPDLOG_LOGGER_DEBUG(logger), SPDLOG_DEBUG()
, 使用宏对以上两种接口进行包装,产生的日志格式包含文件、函数、行。
4.3.2. 文件类型
提供的一些落地的文件类型:
- 标准输出
stdout_sink_base
- 带颜色的标准输出(默认),根据平台选择
wincolor_sink
或ansicolor_sink
- 基本文件
- 可设定时间的滚动文件(按天、按小时)
- 可设定大小的滚动文件
4.3.3. sink
扩展
这里简单提一下 sinks
的实现,所有落地文件的类型都是从 base_sink
(忽略sink) 继承而来,提供了两个纯虚函数 sink_it_()
和 flush_()
,这样一来,需要扩展的类型文件只需要实现两个函数,很大的简化了扩展的流程。而单/多线程通过模板来确定是否需要使用互斥量来保持同步:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct null_mutex
{
void lock() const {}
void unlock() const {}
};
// 例子使用 base_file_sink
using basic_file_sink_mt = basic_file_sink<std::mutex>;
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
template<typename Mutex>
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
{
std::lock_guard<Mutex> lock(mutex_);
sink_it_(msg);
}
如果需要使用多线程模式,就使用 std::mutex
,如果是单线程,我们就不需要互斥来同步,这里实现一个空的互斥量类,就使得 log()
的代码完全不需要修改了,也提高了内聚性。
参考
[1] Home · gabime/spdlog Wiki (github.com)
[2] spdlog 基本结构分析
本文由作者按照 CC BY 4.0 进行授权