Sink是一系列类,功能为负责日志输出的具体实现,其通过继承的方式实现拓展。基类Sink的类定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class SPDLOG_API sink { public: virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
void set_level(level::level_enum log_level); level::level_enum level() const; bool should_log(level::level_enum msg_level) const;
protected: level_t level_{level::trace}; };
|
Sink类的实现
Sink类是一个纯虚类,无法被实例化。在类的声明前包含了SPDLOG_API
宏,这是用于动态库符号导出的宏。
在win平台中,生成动态库时需要使用__declspec(dllexport)
显式声明使得这些符号可以被其他程序使用,这是必须选项。在其他程序使用动态库时,使用__declspec(dllimport)
声明导入了该符号,虽然这不是必须选项,但是有助于理解和编译器优化。
而在其他Linux平台中,编译动态库时默认导出所有符号,可以使用-fvisibility=hidden
编译选项使默认符号不导。为了更详细的控制符号的导出,可以使用__attribute__((visibility("default")))
来设置使符号导出,使用__attribute__ ((visibility("hidden")))
设置使符号不导出。
它一共实现了4个纯虚函数,分别是:
- log:接收传递的日志信息,并输出到具体位置,子类通过重载这个函数来实现各种日志输出功能。
- flush:日志刷新函数,可以调用该函数实现日志的刷新。
- set_pattern:设置日志消息格式
- set_formatter:设置日志消息格式化器
成员变量只有level_,它是std::atomic<int>
类型,可以以比较低的开销实现线程安全性。
base_sink类的实现
Sink类负责定义接口和基本的读写方法,而sink基础的功能在base_sink中实现
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
| template <typename Mutex> class SPDLOG_API base_sink : public sink { public: ****base_sink(); explicit base_sink(std::unique_ptr<spdlog::formatter> formatter); ~base_sink() override = default;
base_sink(const base_sink &) = delete; base_sink(base_sink &&) = delete;
base_sink &operator=(const base_sink &) = delete; base_sink &operator=(base_sink &&) = delete;
void log(const details::log_msg &msg) final override; void flush() final override; void set_pattern(const std::string &pattern) final override; void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final override;
protected: std::unique_ptr<spdlog::formatter> formatter_; Mutex mutex_;
virtual void sink_it_(const details::log_msg &msg) = 0; virtual void flush_() = 0; virtual void set_pattern_(const std::string &pattern); virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter); };
|
base_sink通过删除拷贝构造、移动构造、拷贝赋值和移动赋值来防止sink对象的复制和移动,以确保对象具有独占的所有权或避免资源管理问题。
base_sink类中对重载的函数使用了final
和override
关键字,用于显式声明函数是重载以及不能被再次重载。
base_sink
是一个模板类,模板参数为 Mutex
,通过传递不同类型的 Mutex
,可以实现不同的线程控制功能。传递 std::mutex
时,base_sink
具备线程安全性。如果需要更高的性能,可以传递 spdlog
定义的 null_mutex
。null_mutex
实现了 lock
和 unlock
方法,但它们不执行任何实际操作,因此不会引入锁机制的开销,这个方法通过一套代码实现了线程安全和非线程安全的代码。
1 2 3 4
| struct null_mutex { void lock() const {} void unlock() const {} };
|
如果看base_sink
会发现该类只是实现了一个简单的封装,使用互斥锁来保证线程安全性,具体的实现由新定义的sink_it_
、flush_
、set_pattern_
和set_formatter_
方法实现。
basic_file_sink类的实现
basic_file_sink才是sink的最完整的实现,其关键成员属性details::file_helper file_helper_
负责文件的打开,写入和刷新
1 2 3 4 5 6 7 8 9 10 11
| 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); }
template <typename Mutex> SPDLOG_INLINE void basic_file_sink<Mutex>::flush_() { file_helper_.flush(); }
|
memory_buf_t
是spdlog管理字符缓存的自定义类型,当使用std库时,其为std::string,如果使用fmt库,则使用的是fmt::basic_memory_buffer<char, 250>
,fmt::basic_memory_buffer
通过预分配栈缓冲区和减少动态分配,能够在高效格式化操作中表现更佳,可以根据项目使用的日志长度,调整预分配的大小。
除了实现之外,还封装了使用接口,使用basic_file_sink_mt
和basic_file_sink_st
来区分多线程版和单线程版。
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
| using basic_file_sink_mt = basic_file_sink<std::mutex>; using basic_file_sink_st = basic_file_sink<details::null_mutex>;
}
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); }
template <typename Factory = spdlog::synchronous_factory> inline std::shared_ptr<logger> basic_logger_st(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_st>(logger_name, filename, truncate, event_handlers); }
|
通过工厂方法方便创建logger实例,而不需要单独创建logger对象和sink对象。