diff --git a/.gitignore b/.gitignore index b1a41919..4c2a2bf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Auto generated files -build/* +build/* *.slo *.lo *.o @@ -65,4 +65,7 @@ install_manifest.txt /tests/logs/* # idea -.idea/ \ No newline at end of file +.idea/ + +# vscode +.vscode/ diff --git a/include/spdlog/common.h b/include/spdlog/common.h index dd9a4785..335975e6 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -80,6 +80,7 @@ using log_clock = std::chrono::system_clock; using sink_ptr = std::shared_ptr; using sinks_init_list = std::initializer_list; using log_err_handler = std::function; +using log_custom_flags = std::unordered_map; // string_view type - either std::string_view or fmt::string_view (pre c++17) #if defined(FMT_USE_STD_STRING_VIEW) diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index 69422ba3..53cd1a7d 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -10,15 +10,17 @@ #include #include +#include namespace spdlog { namespace details { struct log_msg { - log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) + log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, const log_custom_flags *flags, string_view_t view) : logger_name(loggers_name) , level(lvl) + , custom_flags(flags) #ifndef SPDLOG_NO_DATETIME , time(os::now()) #endif @@ -31,8 +33,8 @@ struct log_msg { } - log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view) - : log_msg(source_loc{}, loggers_name, lvl, view) + log_msg(const std::string *loggers_name, level::level_enum lvl, const log_custom_flags *flags, string_view_t view) + : log_msg(source_loc{}, loggers_name, lvl, flags, view) { } @@ -40,6 +42,7 @@ struct log_msg const std::string *logger_name{nullptr}; level::level_enum level{level::off}; + const spdlog::log_custom_flags *custom_flags{nullptr}; log_clock::time_point time; size_t thread_id{0}; size_t msg_id{0}; diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index d44652b5..9dbbd580 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -70,8 +70,9 @@ inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const using details::fmt_helper::to_string_view; fmt::memory_buffer buf; fmt::format_to(buf, fmt, args...); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); + details::log_msg log_msg(source, &name_, lvl, &custom_flags_, to_string_view(buf)); sink_it_(log_msg); + } SPDLOG_CATCH_AND_HANDLE } @@ -91,7 +92,7 @@ inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const try { - details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg)); + details::log_msg log_msg(source, &name_, lvl, &custom_flags_, spdlog::string_view_t(msg)); sink_it_(log_msg); } SPDLOG_CATCH_AND_HANDLE @@ -117,7 +118,7 @@ inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const } try { - details::log_msg log_msg(source, &name_, lvl, msg); + details::log_msg log_msg(source, &name_, lvl, &custom_flags_, msg); sink_it_(log_msg); } SPDLOG_CATCH_AND_HANDLE @@ -135,7 +136,7 @@ inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const using details::fmt_helper::to_string_view; fmt::memory_buffer buf; fmt::format_to(buf, "{}", msg); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); + details::log_msg log_msg(source, &name_, lvl, &custom_flags_, to_string_view(buf)); sink_it_(log_msg); } SPDLOG_CATCH_AND_HANDLE @@ -252,7 +253,7 @@ inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const fmt::format_to(wbuf, fmt, args...); fmt::memory_buffer buf; wbuf_to_utf8buf(wbuf, buf); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); + details::log_msg log_msg(source, &name_, lvl, &custom_flags_, to_string_view(buf)); sink_it_(log_msg); } SPDLOG_CATCH_AND_HANDLE @@ -315,6 +316,30 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) level_.store(log_level); } +inline void spdlog::logger::set_custom_flag(char flag, const std::string& value) +{ + custom_flags_[flag] = value; +} + +inline const std::string& spdlog::logger::get_custom_flag(char flag) const +{ + auto it = custom_flags_.find(flag); + if (it != custom_flags_.end()) + { + return it->second; + } + else + { + static std::string empty; + return empty; + } +} + +inline const spdlog::log_custom_flags &spdlog::logger::custom_flags() const +{ + return custom_flags_; +} + inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) { err_handler_ = std::move(err_handler); diff --git a/include/spdlog/details/pattern_formatter.h b/include/spdlog/details/pattern_formatter.h index c0ad86e8..39bca48b 100644 --- a/include/spdlog/details/pattern_formatter.h +++ b/include/spdlog/details/pattern_formatter.h @@ -777,6 +777,38 @@ private: char ch_; }; +class custom_formatter final : public flag_formatter +{ +public: + explicit custom_formatter(padding_info padinfo, char flag) + : flag_formatter(padinfo), flag_(flag){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto it = msg.custom_flags->find(flag_); + if (it != msg.custom_flags->end()) + { + if (padinfo_.enabled()) + { + scoped_pad p(it->second.size(), padinfo_, dest); + fmt_helper::append_string_view(it->second, dest); + } + else + { + fmt_helper::append_string_view(it->second, dest); + } + } + else + { + dest.push_back('%'); + dest.push_back(flag_); + } + } + +private: + char flag_; +}; + // aggregate user chars to display as is class aggregate_formatter final : public flag_formatter { @@ -1241,11 +1273,8 @@ private: formatters_.push_back(details::make_unique('%')); break; - default: // Unknown flag appears as is - auto unknown_flag = details::make_unique(); - unknown_flag->add_ch('%'); - unknown_flag->add_ch(flag); - formatters_.push_back((std::move(unknown_flag))); + default: // Unknown flag is either a custom flag or appears as is + formatters_.push_back(details::make_unique(padding, flag)); break; } } diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index ccd53955..1abaf332 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -198,6 +198,28 @@ public: default_logger_.reset(); } + void set_custom_flag(char flag, const std::string& value) + { + std::lock_guard lock(logger_map_mutex_); + for (auto& l : loggers_) + l.second->set_custom_flag(flag, value); + custom_flags_[flag] = value; + } + + const std::string& get_custom_flag(char flag) + { + auto it = custom_flags_.find(flag); + if (it != custom_flags_.end()) + { + return it->second; + } + else + { + static std::string empty; + return empty; + } + } + // clean all resources and threads started by the registry void shutdown() { @@ -279,6 +301,7 @@ private: std::unique_ptr periodic_flusher_; std::shared_ptr default_logger_; bool automatic_registration_ = true; + spdlog::log_custom_flags custom_flags_; }; } // namespace details diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 35578971..2d747a86 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -104,7 +104,7 @@ struct async_msg // copy into log_msg log_msg to_log_msg() { - log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size())); + log_msg msg(&worker_ptr->name(), level, &worker_ptr->custom_flags(), string_view_t(raw.data(), raw.size())); msg.time = time; msg.thread_id = thread_id; msg.msg_id = msg_id; diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index ec06828f..ce028074 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -1,5 +1,5 @@ // -// Copyright(c) 2015-2108 Gabi Melman. +// Copyright(c) 2015-2019 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // @@ -24,6 +24,7 @@ #include #include +#include #include namespace spdlog { @@ -133,6 +134,11 @@ public: void set_level(level::level_enum log_level); static level::level_enum default_level(); + + void set_custom_flag(char flag, const std::string& value); + const std::string &get_custom_flag(char flag) const; + const log_custom_flags &custom_flags() const; + level::level_enum level() const; const std::string &name() const; @@ -177,6 +183,7 @@ protected: log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }}; std::atomic last_err_time_{0}; std::atomic msg_counter_{1}; + log_custom_flags custom_flags_; }; } // namespace spdlog diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 20ff24be..e5280cb4 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -99,6 +99,18 @@ inline void register_logger(std::shared_ptr logger) details::registry::instance().register_logger(std::move(logger)); } +// Set custom flag +inline void set_custom_flag(char flag, const std::string value) +{ + details::registry::instance().set_custom_flag(flag, std::move(value)); +} + +// Get custom flag +inline const std::string &get_custom_flag(char flag) +{ + return details::registry::instance().get_custom_flag(flag); +} + // Apply a user defined function on all registered loggers // Example: // spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index fb6f75da..8e66413f 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -60,12 +60,12 @@ TEST_CASE("date MM/DD/YY ", "[pattern_formatter]") TEST_CASE("color range test1", "[pattern_formatter]") { auto formatter = std::make_shared("%^%v%$", spdlog::pattern_time_type::local, "\n"); - fmt::memory_buffer buf; fmt::format_to(buf, "Hello"); fmt::memory_buffer formatted; std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, spdlog::string_view_t(buf.data(), buf.size())); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, spdlog::string_view_t(buf.data(), buf.size())); formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_end == 5); @@ -76,7 +76,8 @@ TEST_CASE("color range test2", "[pattern_formatter]") { auto formatter = std::make_shared("%^%$", spdlog::pattern_time_type::local, "\n"); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, ""); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, ""); fmt::memory_buffer formatted; formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 0); @@ -88,7 +89,8 @@ TEST_CASE("color range test3", "[pattern_formatter]") { auto formatter = std::make_shared("%^***%$"); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "ignored"); fmt::memory_buffer formatted; formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 0); @@ -99,8 +101,8 @@ TEST_CASE("color range test4", "[pattern_formatter]") { auto formatter = std::make_shared("XX%^YYY%$", spdlog::pattern_time_type::local, "\n"); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored"); - + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "ignored"); fmt::memory_buffer formatted; formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 2); @@ -112,7 +114,8 @@ TEST_CASE("color range test5", "[pattern_formatter]") { auto formatter = std::make_shared("**%^"); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "ignored"); fmt::memory_buffer formatted; formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 2); @@ -123,13 +126,26 @@ TEST_CASE("color range test6", "[pattern_formatter]") { auto formatter = std::make_shared("**%$"); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "ignored"); fmt::memory_buffer formatted; formatter->format(msg, formatted); REQUIRE(msg.color_range_start == 0); REQUIRE(msg.color_range_end == 2); } +TEST_CASE("custom tag test with padding", "[pattern_formatter]") +{ + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("pattern_tester", oss_sink); + oss_logger.set_level(spdlog::level::info); + oss_logger.set_formatter(std::unique_ptr(new spdlog::pattern_formatter("[%10q] %o %v", spdlog::pattern_time_type::local, "\n"))); + oss_logger.set_custom_flag('q', "custom"); + oss_logger.info("hello, world"); + REQUIRE(oss.str() == "[ custom] %o hello, world\n"); +} + // // Test padding // @@ -200,7 +216,8 @@ TEST_CASE("clone-default-formatter", "[pattern_formatter]") auto formatter_1 = std::make_shared(); auto formatter_2 = formatter_1->clone(); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "some message"); fmt::memory_buffer formatted_1; fmt::memory_buffer formatted_2; @@ -215,7 +232,8 @@ TEST_CASE("clone-default-formatter2", "[pattern_formatter]") auto formatter_1 = std::make_shared("%+"); auto formatter_2 = formatter_1->clone(); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "some message"); fmt::memory_buffer formatted_1; fmt::memory_buffer formatted_2; @@ -230,7 +248,8 @@ TEST_CASE("clone-formatter", "[pattern_formatter]") auto formatter_1 = std::make_shared("%D %X [%] [%n] %v"); auto formatter_2 = formatter_1->clone(); std::string logger_name = "test"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "some message"); fmt::memory_buffer formatted_1; fmt::memory_buffer formatted_2; @@ -245,7 +264,8 @@ TEST_CASE("clone-formatter-2", "[pattern_formatter]") auto formatter_1 = std::make_shared("%D %X [%] [%n] %v", pattern_time_type::utc, "xxxxxx\n"); auto formatter_2 = formatter_1->clone(); std::string logger_name = "test2"; - spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message"); + spdlog::log_custom_flags flags; + spdlog::details::log_msg msg(&logger_name, spdlog::level::info, &flags, "some message"); fmt::memory_buffer formatted_1; fmt::memory_buffer formatted_2;