文章目录
前言
本文将介绍spdlog日志库的整体结构,并对实际使用该日志库的方法进行了分析。
一、spdlog介绍
spdlog 是一款高效的 C++ 日志库,它以其极高的性能和零成本的抽象而著称。spdlog 支持异步和同步日志记录,提供多种日志级别,并允许用户将日志输出到控制台、文件或自定义的接收器。
1.1 spdlog特征
spdlog 的性能特点使其在高性能应用中非常受欢迎
- 极高的日志记录速度: spdlog 能够在每秒记录数百万条日志消息,这对于需要处理大量日志数据的应用来说是非常重要的。
- 低内存占用: spdlog 的设计确保了即使在高负载下,它也能保持低内存占用。
- 灵活的配置: 用户可以根据需要配置 spdlog,选择异步或同步日志记录,以及选择不同的日志级别和输出目标。
1.2 spdlog输出控制
- 多种日志级别:
- trace、debug、info、warn、error,and critical
- 设定最低日志级别,【开发阶段采用较低日志级别,发布阶段采用较高日志级别】
- 多种输出目标: 用户可以将日志输出到控制台、文件或通过网络发送到远程服务器。
- 格式化输出: spdlog 支持格式化输出,允许用户以结构化的方式输出日志消息。
1.3 spdlog处理流程
Spdlog 的流程非常简单,但是每个组件都扮演着重要的角色。
Loggers 负责记录日志消息;
Sinks 决定了日志消息的输出位置;
Formatters 负责将日志消息转换为特定格式;
Async Logger 异步地将日志消息写入到目标 Sink 中;
Registry 用于管理这些组件。

异步logger使用了线程池,默认一个线程。
可能采用多个logger,比如网络logger、本地logger、定制logger等
1.4 异步日志和同步日志区别
1.4.1 多线程使用日志库,跟同步和异步是否有关联?
没有什么关联,当同步打印日志时,但我们是使用的多线程,此时我们需要考虑使用互斥锁只允许一个线程操作。当异步打印日志时,那么执行异步操作的线程也需要考虑互斥的问题。
1.4.2 同一线程处理,是不是就是同步?
如果是当前线程处理,当然是同步的。但是如果只是获得完成通知,那么就只是协程上的同步。
二、异步日志库设计与实现
2.1 如何创建logger
// 生成默认的logger、sink等,该信息会输出到控制台!
spdlog::info("Welcome to spdlog!");
2.1.1 工厂方法创建
auto logger = spdlog::basic_logger_mt("basic", "basic.txt");
logger->info("hello world from basic_sink");
spdlog::get("basic")->info("hello world from basic_sink tool");
// 异步工厂
auto logger2 = spdlog::basic_logger_mt<spdlog::async_factory>("async_basic", "basic.txt");
logger2->info("hello world from basic_sink");
工厂模式的好处:
某一个对象创建较复杂,将创建过程对用户进行屏蔽。
template <typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name,
const filename_t &filename,
bool truncate = false,
const file_event_handlers &event_handlers = {}) {
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate,
event_handlers);
}
该工厂方法屏蔽了logger对register和sink等对象的依赖。
2.1.2 手动创建
auto sink1 = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
auto sink2 = std::make_shared<spdlog::sinks::basic_file_sink_mt>("hand.txt", true);
auto logger3 = std::make_shared<spdlog::logger>("self");
logger3->sinks().push_back(sink1);
logger3->sinks().push_back(sink2);
spdlog::register_logger(logger3); // 将logger需要手动注册到register中
logger3->info("multi sinks");
手动创建可以绑定多个sink。
注册logger,目的是为了全局访问
2.2 如何创建sink
sink_it_
template <typename Mutex>
SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg) {
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
file_helper_.write(formatted);
}
是为了写到用户态的缓冲区
flush_
template <typename Mutex>
SPDLOG_INLINE void basic_file_sink<Mutex>::flush_() {
file_helper_.flush();
}
是写到内核态的发送缓冲区
2.3 如何自定义格式化
set_pattern
eg.
sink1->set_pattern("[%^%L%$] %v");
2.4 如何创建异步日志
// 1. 使用spdlog::async_factory模板参数
auto logger2 = spdlog::basic_logger_mt<spdlog::async_factory>("async_basic", "basic.txt");
// 2. 使用spdlog::create_async<Sink>
spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async", "hello.txt", true);
// 3. 使用spdlog::create_async_nb<Sink>,队列是非阻塞的。
// 4. 使用spdlog::async_logger
auto async_logger = std::make_shared<spdlog::async_logger>("tp", spdlog::sinks_init_list{sink1, sink2}, spdlog::thread_pool());
aysnc_logger->info("async_logger");

2.5 刷新策略
// 间隔flush,需要线程安全的logger
spdlog::flush_every(std::chrono::seconds(5))
// 条件flush,设置最小日志等级触发自动flush
logger->flush_on(spdlog::level::err);
// 手动flush,如果是异步的,将发出一个flush的消息
logger->flush
三、日志的作用
- 追踪程序运行状态
- 出现问题提供现场运行信息
总结
本文总结了spdlog日志库的整体结构,并列举实际使用该日志库的方法。
参考链接:
https://github.com/0voice
2937

被折叠的 条评论
为什么被折叠?



