diff --git a/example/example.cpp b/example/example.cpp index a920c6de..ed95db94 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -390,7 +390,7 @@ void attribute_example() { // auto logfmt_logger = spdlog::basic_logger_mt("logfmt_logger", "logs/mylog.txt"); auto logfmt_logger = spdlog::stdout_color_mt("logfmt_logger"); - std::string logfmt_pattern = "time=%Y-%m-%dT%H:%M:%S.%f%z name=%n level=%^%l%$ process=%P thread=%t message=\"%v\" "; + std::string logfmt_pattern = "time=%Y-%m-%dT%H:%M:%S.%f%z name=%n level=%^%l%$ process=%P thread=%t message=\"%v\"%( %K=\"%V\"%)"; logfmt_logger->set_pattern(logfmt_pattern); logfmt_logger->info("logfmt structured logging", spdlog::attribute_list{{"key\n1", "value\n1"}, {"key\r\n2", "value\r\n2"}}); diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index ba866c68..cceef76d 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -929,6 +929,54 @@ private: log_clock::time_point last_message_time_; }; +// attribute formatting: stub +class attr_formatter_start final : public flag_formatter +{ +public: + explicit attr_formatter_start(padding_info padinfo) + : flag_formatter(padinfo, details::attr_flags::start) + {} + void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override + { + fmt_helper::append_string_view("", dest); + } +}; +class attr_formatter_stop final : public flag_formatter +{ +public: + explicit attr_formatter_stop(padding_info padinfo) + : flag_formatter(padinfo, details::attr_flags::stop) + {} + void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override + { + fmt_helper::append_string_view("", dest); + } +}; + +class attr_formatter_key final : public flag_formatter +{ +public: + explicit attr_formatter_key(padding_info padinfo) + : flag_formatter(padinfo, details::attr_flags::key) + {} + void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override + { + fmt_helper::append_string_view("", dest); + } +}; + +class attr_formatter_value final : public flag_formatter +{ +public: + explicit attr_formatter_value(padding_info padinfo) + : flag_formatter(padinfo, details::attr_flags::value) + {} + void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override + { + fmt_helper::append_string_view("", dest); + } +}; + // Full info formatter // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v class full_formatter final : public flag_formatter @@ -1012,7 +1060,14 @@ public: fmt_helper::append_string_view(msg.payload, dest); if (msg.attributes.size() > 0) { + // by default uses logfmt-esque kv pairs fmt_helper::append_string_view(" | ", dest); // separate message from attributes + for (const details::attr& a : msg.attributes) { + details::fmt_helper::append_string_view(a.key, dest); + details::fmt_helper::append_string_view("=\"", dest); + details::fmt_helper::append_string_view(a.value, dest); + details::fmt_helper::append_string_view("\" ", dest); + } } } @@ -1076,16 +1131,33 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory } } - for (auto &f : formatters_) + auto it_end = formatters_.begin(); + for (auto it = formatters_.begin(); it != formatters_.end(); ++it) { - f->format(msg, cached_tm_, dest); - } - // TODO: make separate function, and add a custom format to attributes (for now just using logfmt) - for (const details::attr& a : msg.attributes) { - details::fmt_helper::append_string_view(a.key, dest); - details::fmt_helper::append_string_view("=\"", dest); - details::fmt_helper::append_string_view(a.value, dest); - details::fmt_helper::append_string_view("\" ", dest); + if ((*it)->flag_ == details::attr_flags::start) { + if (msg.attributes.size() == 0) { + while((*it)->flag_ != details::attr_flags::stop && it != formatters_.end()) ++it; + it_end = it; + } + for (const details::attr& a : msg.attributes) { + for (auto it2 = it; it2 != formatters_.end(); ++it2) { + if ((*it2)->flag_ == details::attr_flags::stop) { + it_end = it2; + break; + } else if ((*it2)->flag_ == details::attr_flags::key) { + // custom formatting function overload makes this even more messy with reinterpret casts, + // will just do manual key addition + details::fmt_helper::append_string_view(a.key, dest); + } else if ((*it2)->flag_ == details::attr_flags::value) { + details::fmt_helper::append_string_view(a.value, dest); + } else { + (*it2)->format(msg, cached_tm_, dest); + } + } + } + it = it_end; + } + (*it)->format(msg, cached_tm_, dest); } // write eol @@ -1320,6 +1392,22 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i formatters_.push_back(details::make_unique>(padding)); break; + case ('('): // start attribute formatting + formatters_.push_back(details::make_unique(padding)); + break; + + case ('K'): // attribute key + formatters_.push_back(details::make_unique(padding)); + break; + + case ('V'): // attribute value + formatters_.push_back(details::make_unique(padding)); + break; + + case (')'): // stop attribute formatting + formatters_.push_back(details::make_unique(padding)); + break; + default: // Unknown flag appears as is auto unknown_flag = details::make_unique(); diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index acf1c536..ad7dc8f5 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -47,16 +47,30 @@ struct padding_info bool enabled_ = false; }; +enum class attr_flags { + none, + start, + stop, + key, + value +}; + class SPDLOG_API flag_formatter { public: explicit flag_formatter(padding_info padinfo) : padinfo_(padinfo) {} + explicit flag_formatter(padding_info padinfo, attr_flags flag) + : flag_(flag) + , 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; + attr_flags flag_ = attr_flags::none; + protected: padding_info padinfo_; };