diff --git a/include/spdlog/details/pattern_formatter-inl.h b/include/spdlog/details/pattern_formatter-inl.h index 5fcce29e..f4efcc23 100644 --- a/include/spdlog/details/pattern_formatter-inl.h +++ b/include/spdlog/details/pattern_formatter-inl.h @@ -974,11 +974,13 @@ private: } // namespace details -SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern, pattern_time_type time_type, std::string eol) +SPDLOG_INLINE pattern_formatter::pattern_formatter( + std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags) : pattern_(std::move(pattern)) , eol_(std::move(eol)) , pattern_time_type_(time_type) , last_log_secs_(0) + , custom_handlers_(std::move(custom_user_flags)) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); compile_pattern_(pattern_); @@ -997,7 +999,12 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const { - return details::make_unique(pattern_, pattern_time_type_, eol_); + custom_flags cloned_custom_formatters; + for (auto &it : custom_handlers_) + { + cloned_custom_formatters[it.first] = it.second->clone(); + } + return details::make_unique(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters)); } SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) @@ -1017,6 +1024,11 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory details::fmt_helper::append_string_view(eol_, dest); } +SPDLOG_INLINE void pattern_formatter::recompile() +{ + compile_pattern_(pattern_); +} + SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) { if (pattern_time_type_ == pattern_time_type::local) @@ -1029,6 +1041,12 @@ SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) template SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding) { + auto it = custom_handlers_.find(flag); + if (it != custom_handlers_.end()) + { + formatters_.push_back(it->second->clone()); + return; + } switch (flag) { diff --git a/include/spdlog/details/pattern_formatter.h b/include/spdlog/details/pattern_formatter.h index 820f55b5..574523c5 100644 --- a/include/spdlog/details/pattern_formatter.h +++ b/include/spdlog/details/pattern_formatter.h @@ -14,6 +14,7 @@ #include #include +#include namespace spdlog { namespace details { @@ -46,7 +47,7 @@ struct padding_info bool enabled_ = false; }; -class flag_formatter +class SPDLOG_API flag_formatter { public: explicit flag_formatter(padding_info padinfo) @@ -62,11 +63,19 @@ protected: } // namespace details +class SPDLOG_API custom_flag_formatter : public details::flag_formatter +{ +public: + virtual std::unique_ptr clone() const = 0; +}; + class SPDLOG_API pattern_formatter final : public formatter { public: - explicit pattern_formatter( - std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); + using custom_flags = std::unordered_map>; + + 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 = {}); // use default pattern is not given explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); @@ -77,6 +86,14 @@ public: std::unique_ptr clone() const override; void format(const details::log_msg &msg, memory_buf_t &dest) override; + template + pattern_formatter &add_flag_handler(char flag, const Args &... args) + { + custom_handlers_[flag] = details::make_unique(args...); + return *this; + } + void recompile(); + private: std::string pattern_; std::string eol_; @@ -84,6 +101,7 @@ private: std::tm cached_tm_; std::chrono::seconds last_log_secs_; std::vector> formatters_; + custom_flags custom_handlers_; std::tm get_time_(const details::log_msg &msg); template diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index d9647f9b..04247bc5 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -246,6 +246,8 @@ TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]") REQUIRE(lines[1] == "message [funct]"); } + + TEST_CASE("clone-default-formatter", "[pattern_formatter]") { auto formatter_1 = std::make_shared(); @@ -305,6 +307,43 @@ TEST_CASE("clone-formatter-2", "[pattern_formatter]") REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2)); } + +class custom_test_flag : public spdlog::custom_flag_formatter +{ +public: + class custom_test_flag (std::string txt) : some_txt{std::move(txt)} + {} + + void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override + { + dest.append(some_txt.data(), some_txt.data() + some_txt.size()); + } + + std::string some_txt; + + std::unique_ptr clone() const override + { + return spdlog::details::make_unique(some_txt); + } +}; +// test clone with custom flag formatters +TEST_CASE("clone-custom_formatter", "[pattern_formatter]") +{ + auto formatter_1 = std::make_shared("[%n] [%t] %v", spdlog::pattern_time_type::utc, ""); + formatter_1->add_flag_handler('t', "custom_output").recompile(); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "logger-name"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + REQUIRE(fmt::to_string(formatted_1) == "[logger-name] [custom_output] some message"); + REQUIRE(fmt::to_string(formatted_2) == "[logger-name] [custom_output] some message"); +} + // // Test source location formatting // @@ -358,3 +397,16 @@ TEST_CASE("full filename formatter", "[pattern_formatter]") formatter.format(msg, formatted); REQUIRE(fmt::to_string(formatted) == test_path); } + + +TEST_CASE("custom flags", "[pattern_formatter]") +{ + auto formatter = std::make_shared("[%n] [%t] [%u] %v", spdlog::pattern_time_type::utc, ""); + formatter->add_flag_handler('t', "custom1").add_flag_handler('u', "custom2").recompile(); + + memory_buf_t formatted; + + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, "some message"); + formatter->format(msg, formatted); + REQUIRE(fmt::to_string(formatted) == "[logger-name] [custom1] [custom2] some message"); +}