Formatter {#Formatter}
Formatter
负责将日志格式化为字符串。Spdlog
提供了多种Formatter
,比如pattern_formatter
(按指定的格式输出日志)、json_formatter
(以JSON格式输出日志)等。Formatter
的定义如下:
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| class formatter { public: virtual ~formatter() = default; virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0; virtual std::unique_ptr<formatter> clone() const = 0; };
|
formatter
是一个抽象类,它包含一个format()
方法用于将日志消息格式化为字符串。不同的Formatter
实现会按照不同的格式输出日志。
实际在使用的是 pattern_formatter
(继承自formatter
),在base_sink
的默认构造函数里为我们初始化好了。
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4
| template<typename Mutex> SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink() : formatter_{details::make_unique<spdlog::pattern_formatter>()} {}
|
pattern_formatter {#pattern-formatter}
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| class SPDLOG_API pattern_formatter final : public formatter { public: using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>; explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags()); // ... private: // ... std::vector<std::unique_ptr<details::flag_formatter>> formatters_; };
|
pattern_formatter
里提供了一组格式化器 formatters_
每个格式化器都继承基类 flag_formatter
实现特定的格式化功能。spdlog
提供的格式化器主要有:
- level_formatter(输出日志级别)
- short_level_formatter(输出日志级别的缩写)
- datetime_formatter(输出日期和时间)
- message_formatter(输出日志消息)
- color_start_formatter和color_stop_formatter(输出带颜色的日志) -源代码位置
- name_formatter (输出日志器名称)
- color_start_bold_formatter 和 color_stop_formatter(输出带颜色和加粗的日志)
- source_location_formatter (获取源文件地址)
- thread_id_formatter(输出线程ID)
- process_id_formatter(输出进程ID)
- file_formatter(输出源代码文件名)
- line_formatter(输出源代码行号)
通过继承 flag_formatter 也可以实现自定义的格式化器,带来更多的灵活性。
|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| class SPDLOG_API flag_formatter { public: explicit flag_formatter(padding_info padinfo) : padinfo_(padinfo) {} flag_formatter() = default; virtual ~flag_formatter() = default; virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0; protected: padding_info padinfo_; };
|
在调用设置格式化字符串函数 set_pattern
的时候,pattern_formatter
会解析字符串,将字符串转换成不同的格式化器添加到 formatters_
里面。具体的逻辑在 compile_pattern_
函数实现:
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern) { auto end = pattern.end(); std::unique_ptr<details::aggregate_formatter> user_chars; formatters_.clear(); for (auto it = pattern.begin(); it != end; ++it) { if (*it == '%') { if (user_chars) // append user chars found so far { formatters_.push_back(std::move(user_chars)); } auto padding = handle_padspec_(++it, end); if (it != end) { if (padding.enabled()) { handle_flag_<details::scoped_padder>(*it, padding); } else { handle_flag_<details::null_scoped_padder>(*it, padding); } } else { break; } } else // chars not following the % sign should be displayed as is { if (!user_chars) { user_chars = details::make_unique<details::aggregate_formatter>(); } user_chars->add_ch(*it); } } if (user_chars) // append raw chars found so far { formatters_.push_back(std::move(user_chars)); } }
|
代码的核心原理就是遍历字符串解析成指定的格式化器。
在sink
写日志的时候,调用格式化器格式化,如 basic_file_sink
:
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| 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); }
|
不过这里有个疑惑:format
格式化为什么写在 base_sink
基类里呢?,这样省得每个派生的sink
都要写一遍 format
格式化代码。
参考链接:https://zhuanlan.zhihu.com/p/620251465