51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

spdlog日志库的核心组件分析-formatter

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


赞(1)
未经允许不得转载:工具盒子 » spdlog日志库的核心组件分析-formatter