diff options
Diffstat (limited to 'matching/include/spdlog/details/pattern_formatter.h')
-rw-r--r-- | matching/include/spdlog/details/pattern_formatter.h | 1336 |
1 files changed, 1336 insertions, 0 deletions
diff --git a/matching/include/spdlog/details/pattern_formatter.h b/matching/include/spdlog/details/pattern_formatter.h new file mode 100644 index 0000000..c0ad86e --- /dev/null +++ b/matching/include/spdlog/details/pattern_formatter.h @@ -0,0 +1,1336 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "spdlog/details/fmt_helper.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/os.h" +#include "spdlog/fmt/fmt.h" +#include "spdlog/formatter.h" + +#include <array> +#include <chrono> +#include <ctime> +#include <cctype> +#include <memory> +#include <mutex> +#include <string> +#include <thread> +#include <utility> +#include <vector> + +namespace spdlog { +namespace details { + +// padding information. +struct padding_info +{ + enum pad_side + { + left, + right, + center + }; + + padding_info() = default; + padding_info(size_t width, padding_info::pad_side side) + : width_(width) + , side_(side) + { + } + + bool enabled() const + { + return width_ != 0; + } + const size_t width_ = 0; + const pad_side side_ = left; +}; + +class scoped_pad +{ +public: + scoped_pad(size_t wrapped_size, padding_info &padinfo, fmt::memory_buffer &dest) + : padinfo_(padinfo) + , dest_(dest) + { + + if (padinfo_.width_ <= wrapped_size) + { + total_pad_ = 0; + return; + } + + total_pad_ = padinfo.width_ - wrapped_size; + if (padinfo_.side_ == padding_info::left) + { + pad_it(total_pad_); + total_pad_ = 0; + } + else if (padinfo_.side_ == padding_info::center) + { + auto half_pad = total_pad_ / 2; + auto reminder = total_pad_ & 1; + pad_it(half_pad); + total_pad_ = half_pad + reminder; // for the right side + } + } + + scoped_pad(spdlog::string_view_t txt, padding_info &padinfo, fmt::memory_buffer &dest) + : scoped_pad(txt.size(), padinfo, dest) + { + } + + ~scoped_pad() + { + if (total_pad_) + { + pad_it(total_pad_); + } + } + +private: + void pad_it(size_t count) + { + // count = std::min(count, spaces_.size()); + assert(count <= spaces_.size()); + fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_); + } + + const padding_info &padinfo_; + fmt::memory_buffer &dest_; + size_t total_pad_; + string_view_t spaces_{" " + " ", + 128}; +}; + +class 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, fmt::memory_buffer &dest) = 0; + +protected: + padding_info padinfo_; +}; + +/////////////////////////////////////////////////////////////////////// +// name & level pattern appender +/////////////////////////////////////////////////////////////////////// +class name_formatter : public flag_formatter +{ +public: + explicit name_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + scoped_pad p(*msg.logger_name, padinfo_, dest); + fmt_helper::append_string_view(*msg.logger_name, dest); + } + else + { + fmt_helper::append_string_view(*msg.logger_name, dest); + } + } +}; + +// log level appender +class level_formatter : public flag_formatter +{ +public: + explicit level_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + string_view_t &level_name = level::to_string_view(msg.level); + if (padinfo_.enabled()) + { + scoped_pad p(level_name, padinfo_, dest); + fmt_helper::append_string_view(level_name, dest); + } + else + { + fmt_helper::append_string_view(level_name, dest); + } + } +}; + +// short log level appender +class short_level_formatter : public flag_formatter +{ +public: + explicit short_level_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + string_view_t level_name{level::to_short_c_str(msg.level)}; + scoped_pad p(level_name, padinfo_, dest); + fmt_helper::append_string_view(level_name, dest); + } +}; + +/////////////////////////////////////////////////////////////////////// +// Date time pattern appenders +/////////////////////////////////////////////////////////////////////// + +static const char *ampm(const tm &t) +{ + return t.tm_hour >= 12 ? "PM" : "AM"; +} + +static int to12h(const tm &t) +{ + return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; +} + +// Abbreviated weekday name +static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +class a_formatter : public flag_formatter +{ +public: + explicit a_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{days[tm_time.tm_wday]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Full weekday name +static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +class A_formatter : public flag_formatter +{ +public: + explicit A_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{full_days[tm_time.tm_wday]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Abbreviated month +static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; +class b_formatter : public flag_formatter +{ +public: + explicit b_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{months[tm_time.tm_mon]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Full month name +static const char *full_months[]{ + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; +class B_formatter : public flag_formatter +{ +public: + explicit B_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{full_months[tm_time.tm_mon]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Date and time representation (Thu Aug 23 15:35:46 2014) +class c_formatter final : public flag_formatter +{ +public: + explicit c_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 24; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::append_string_view(days[tm_time.tm_wday], dest); + dest.push_back(' '); + fmt_helper::append_string_view(months[tm_time.tm_mon], dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_mday, dest); + dest.push_back(' '); + // time + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// year - 2 digit +class C_formatter final : public flag_formatter +{ +public: + explicit C_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 +class D_formatter final : public flag_formatter +{ +public: + explicit D_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 10; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_mday, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// year - 4 digit +class Y_formatter final : public flag_formatter +{ +public: + explicit Y_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 4; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// month 1-12 +class m_formatter final : public flag_formatter +{ +public: + explicit m_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + } +}; + +// day of month 1-31 +class d_formatter final : public flag_formatter +{ +public: + explicit d_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_mday, dest); + } +}; + +// hours in 24 format 0-23 +class H_formatter final : public flag_formatter +{ +public: + explicit H_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_hour, dest); + } +}; + +// hours in 12 format 1-12 +class I_formatter final : public flag_formatter +{ +public: + explicit I_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(to12h(tm_time), dest); + } +}; + +// minutes 0-59 +class M_formatter final : public flag_formatter +{ +public: + explicit M_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// seconds 0-59 +class S_formatter final : public flag_formatter +{ +public: + explicit S_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// milliseconds +class e_formatter final : public flag_formatter +{ +public: + explicit e_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 3; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); + } + else + { + fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); + } + } +}; + +// microseconds +class f_formatter final : public flag_formatter +{ +public: + explicit f_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 6; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad6(static_cast<size_t>(micros.count()), dest); + } + else + { + fmt_helper::pad6(static_cast<size_t>(micros.count()), dest); + } + } +}; + +// nanoseconds +class F_formatter final : public flag_formatter +{ +public: + explicit F_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 9; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad9(static_cast<size_t>(ns.count()), dest); + } + else + { + fmt_helper::pad9(static_cast<size_t>(ns.count()), dest); + } + } +}; + +// seconds since epoch +class E_formatter final : public flag_formatter +{ +public: + explicit E_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + const size_t field_size = 10; + scoped_pad p(field_size, padinfo_, dest); + auto duration = msg.time.time_since_epoch(); + auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count(); + fmt_helper::append_int(seconds, dest); + } +}; + +// AM/PM +class p_formatter final : public flag_formatter +{ +public: + explicit p_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_string_view(ampm(tm_time), dest); + } +}; + +// 12 hour clock 02:55:02 pm +class r_formatter final : public flag_formatter +{ +public: + explicit r_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 11; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(to12h(tm_time), dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_string_view(ampm(tm_time), dest); + } +}; + +// 24-hour HH:MM time, equivalent to %H:%M +class R_formatter final : public flag_formatter +{ +public: + explicit R_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 5; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S +class T_formatter final : public flag_formatter +{ +public: + explicit T_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 8; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// ISO 8601 offset from UTC in timezone (+-HH:MM) +class z_formatter final : public flag_formatter +{ +public: + explicit z_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + const std::chrono::seconds cache_refresh = std::chrono::seconds(5); + + z_formatter() = default; + z_formatter(const z_formatter &) = delete; + z_formatter &operator=(const z_formatter &) = delete; + + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 6; + scoped_pad p(field_size, padinfo_, dest); + +#ifdef _WIN32 + int total_minutes = get_cached_offset(msg, tm_time); +#else + // No need to chache under gcc, + // it is very fast (already stored in tm.tm_gmtoff) + (void)(msg); + int total_minutes = os::utc_minutes_offset(tm_time); +#endif + bool is_negative = total_minutes < 0; + if (is_negative) + { + total_minutes = -total_minutes; + dest.push_back('-'); + } + else + { + dest.push_back('+'); + } + + fmt_helper::pad2(total_minutes / 60, dest); // hours + dest.push_back(':'); + fmt_helper::pad2(total_minutes % 60, dest); // minutes + } + +private: + log_clock::time_point last_update_{std::chrono::seconds(0)}; +#ifdef _WIN32 + int offset_minutes_{0}; + + int get_cached_offset(const log_msg &msg, const std::tm &tm_time) + { + if (msg.time - last_update_ >= cache_refresh) + { + offset_minutes_ = os::utc_minutes_offset(tm_time); + last_update_ = msg.time; + } + return offset_minutes_; + } +#endif +}; + +// Thread id +class t_formatter final : public flag_formatter +{ +public: + explicit t_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + const auto field_size = fmt_helper::count_digits(msg.thread_id); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(msg.thread_id, dest); + } + else + { + fmt_helper::append_int(msg.thread_id, dest); + } + } +}; + +// Current pid +class pid_formatter final : public flag_formatter +{ +public: + explicit pid_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + const auto pid = static_cast<uint32_t>(details::os::pid()); + if (padinfo_.enabled()) + { + auto field_size = fmt_helper::count_digits(pid); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(pid, dest); + } + else + { + fmt_helper::append_int(pid, dest); + } + } +}; + +// message counter formatter +class i_formatter final : public flag_formatter +{ +public: + explicit i_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + const size_t field_size = 6; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad6(msg.msg_id, dest); + } +}; + +class v_formatter final : public flag_formatter +{ +public: + explicit v_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + scoped_pad p(msg.payload, padinfo_, dest); + fmt_helper::append_string_view(msg.payload, dest); + } + else + { + fmt_helper::append_string_view(msg.payload, dest); + } + } +}; + +class ch_formatter final : public flag_formatter +{ +public: + explicit ch_formatter(char ch) + : ch_(ch) + { + } + + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + const size_t field_size = 1; + scoped_pad p(field_size, padinfo_, dest); + dest.push_back(ch_); + } + +private: + char ch_; +}; + +// aggregate user chars to display as is +class aggregate_formatter final : public flag_formatter +{ +public: + aggregate_formatter() = default; + + void add_ch(char ch) + { + str_ += ch; + } + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_string_view(str_, dest); + } + +private: + std::string str_; +}; + +// mark the color range. expect it to be in the form of "%^colored text%$" +class color_start_formatter final : public flag_formatter +{ +public: + explicit color_start_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_start = dest.size(); + } +}; +class color_stop_formatter final : public flag_formatter +{ +public: + explicit color_stop_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_end = dest.size(); + } +}; + +// print source location +class source_location_formatter final : public flag_formatter +{ +public: + explicit source_location_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + if (padinfo_.enabled()) + { + const auto text_size = std::char_traits<char>::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1; + scoped_pad p(text_size, padinfo_, dest); + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + } + else + { + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + } + } +}; +// print source filename +class source_filename_formatter final : public flag_formatter +{ +public: + explicit source_filename_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + scoped_pad p(msg.source.filename, padinfo_, dest); + fmt_helper::append_string_view(msg.source.filename, dest); + } +}; + +class source_linenum_formatter final : public flag_formatter +{ +public: + explicit source_linenum_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + if (padinfo_.enabled()) + { + auto field_size = fmt_helper::count_digits(msg.source.line); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(msg.source.line, dest); + } + else + { + fmt_helper::append_int(msg.source.line, dest); + } + } +}; +// print source funcname +class source_funcname_formatter final : public flag_formatter +{ +public: + explicit source_funcname_formatter(padding_info padinfo) + : flag_formatter(padinfo){}; + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + scoped_pad p(msg.source.funcname, padinfo_, dest); + fmt_helper::append_string_view(msg.source.funcname, dest); + } +}; + +// Full info formatter +// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v +class full_formatter final : public flag_formatter +{ +public: + explicit full_formatter(padding_info padinfo) + : flag_formatter(padinfo) + { + } + + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + +#ifndef SPDLOG_NO_DATETIME + + // cache the date/time part for the next second. + auto duration = msg.time.time_since_epoch(); + auto secs = duration_cast<seconds>(duration); + + if (cache_timestamp_ != secs || cached_datetime_.size() == 0) + { + cached_datetime_.clear(); + cached_datetime_.push_back('['); + fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); + cached_datetime_.push_back(' '); + + fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_min, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); + cached_datetime_.push_back('.'); + + cache_timestamp_ = secs; + } + fmt_helper::append_buf(cached_datetime_, dest); + + auto millis = fmt_helper::time_fraction<milliseconds>(msg.time); + fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); + dest.push_back(']'); + dest.push_back(' '); + +#else // no datetime needed + (void)tm_time; +#endif + +#ifndef SPDLOG_NO_NAME + if (!msg.logger_name->empty()) + { + dest.push_back('['); + // fmt_helper::append_str(*msg.logger_name, dest); + fmt_helper::append_string_view(*msg.logger_name, dest); + dest.push_back(']'); + dest.push_back(' '); + } +#endif + + dest.push_back('['); + // wrap the level name with color + msg.color_range_start = dest.size(); + // fmt_helper::append_string_view(level::to_c_str(msg.level), dest); + fmt_helper::append_string_view(level::to_string_view(msg.level), dest); + msg.color_range_end = dest.size(); + dest.push_back(']'); + dest.push_back(' '); + + // add source location if present + if (!msg.source.empty()) + { + dest.push_back('['); + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + dest.push_back(']'); + dest.push_back(' '); + } + // fmt_helper::append_string_view(msg.msg(), dest); + fmt_helper::append_string_view(msg.payload, dest); + } + +private: + std::chrono::seconds cache_timestamp_{0}; + fmt::basic_memory_buffer<char, 128> cached_datetime_; +}; + +} // namespace details + +class 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) + : pattern_(std::move(pattern)) + , eol_(std::move(eol)) + , pattern_time_type_(time_type) + , last_log_secs_(0) + { + std::memset(&cached_tm_, 0, sizeof(cached_tm_)); + compile_pattern_(pattern_); + } + + // use by default full formatter for if pattern is not given + explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol) + : pattern_("%+") + , eol_(std::move(eol)) + , pattern_time_type_(time_type) + , last_log_secs_(0) + { + std::memset(&cached_tm_, 0, sizeof(cached_tm_)); + formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{})); + } + + pattern_formatter(const pattern_formatter &other) = delete; + pattern_formatter &operator=(const pattern_formatter &other) = delete; + + std::unique_ptr<formatter> clone() const override + { + return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_); + } + + void format(const details::log_msg &msg, fmt::memory_buffer &dest) override + { +#ifndef SPDLOG_NO_DATETIME + auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch()); + if (secs != last_log_secs_) + { + cached_tm_ = get_time_(msg); + last_log_secs_ = secs; + } +#endif + for (auto &f : formatters_) + { + f->format(msg, cached_tm_, dest); + } + // write eol + details::fmt_helper::append_string_view(eol_, dest); + } + +private: + std::string pattern_; + std::string eol_; + pattern_time_type pattern_time_type_; + std::tm cached_tm_; + std::chrono::seconds last_log_secs_; + + std::vector<std::unique_ptr<details::flag_formatter>> formatters_; + + std::tm get_time_(const details::log_msg &msg) + { + if (pattern_time_type_ == pattern_time_type::local) + { + return details::os::localtime(log_clock::to_time_t(msg.time)); + } + return details::os::gmtime(log_clock::to_time_t(msg.time)); + } + + void handle_flag_(char flag, details::padding_info padding) + { + switch (flag) + { + + case ('+'): // default formatter + formatters_.push_back(details::make_unique<details::full_formatter>(padding)); + break; + + case 'n': // logger name + formatters_.push_back(details::make_unique<details::name_formatter>(padding)); + break; + + case 'l': // level + formatters_.push_back(details::make_unique<details::level_formatter>(padding)); + break; + + case 'L': // short level + formatters_.push_back(details::make_unique<details::short_level_formatter>(padding)); + break; + + case ('t'): // thread id + formatters_.push_back(details::make_unique<details::t_formatter>(padding)); + break; + + case ('v'): // the message text + formatters_.push_back(details::make_unique<details::v_formatter>(padding)); + break; + + case ('a'): // weekday + formatters_.push_back(details::make_unique<details::a_formatter>(padding)); + break; + + case ('A'): // short weekday + formatters_.push_back(details::make_unique<details::A_formatter>(padding)); + break; + + case ('b'): + case ('h'): // month + formatters_.push_back(details::make_unique<details::b_formatter>(padding)); + break; + + case ('B'): // short month + formatters_.push_back(details::make_unique<details::B_formatter>(padding)); + break; + + case ('c'): // datetime + formatters_.push_back(details::make_unique<details::c_formatter>(padding)); + break; + + case ('C'): // year 2 digits + formatters_.push_back(details::make_unique<details::C_formatter>(padding)); + break; + + case ('Y'): // year 4 digits + formatters_.push_back(details::make_unique<details::Y_formatter>(padding)); + break; + + case ('D'): + case ('x'): // datetime MM/DD/YY + formatters_.push_back(details::make_unique<details::D_formatter>(padding)); + break; + + case ('m'): // month 1-12 + formatters_.push_back(details::make_unique<details::m_formatter>(padding)); + break; + + case ('d'): // day of month 1-31 + formatters_.push_back(details::make_unique<details::d_formatter>(padding)); + break; + + case ('H'): // hours 24 + formatters_.push_back(details::make_unique<details::H_formatter>(padding)); + break; + + case ('I'): // hours 12 + formatters_.push_back(details::make_unique<details::I_formatter>(padding)); + break; + + case ('M'): // minutes + formatters_.push_back(details::make_unique<details::M_formatter>(padding)); + break; + + case ('S'): // seconds + formatters_.push_back(details::make_unique<details::S_formatter>(padding)); + break; + + case ('e'): // milliseconds + formatters_.push_back(details::make_unique<details::e_formatter>(padding)); + break; + + case ('f'): // microseconds + formatters_.push_back(details::make_unique<details::f_formatter>(padding)); + break; + + case ('F'): // nanoseconds + formatters_.push_back(details::make_unique<details::F_formatter>(padding)); + break; + + case ('E'): // seconds since epoch + formatters_.push_back(details::make_unique<details::E_formatter>(padding)); + break; + + case ('p'): // am/pm + formatters_.push_back(details::make_unique<details::p_formatter>(padding)); + break; + + case ('r'): // 12 hour clock 02:55:02 pm + formatters_.push_back(details::make_unique<details::r_formatter>(padding)); + break; + + case ('R'): // 24-hour HH:MM time + formatters_.push_back(details::make_unique<details::R_formatter>(padding)); + break; + + case ('T'): + case ('X'): // ISO 8601 time format (HH:MM:SS) + formatters_.push_back(details::make_unique<details::T_formatter>(padding)); + break; + + case ('z'): // timezone + formatters_.push_back(details::make_unique<details::z_formatter>(padding)); + break; + + case ('P'): // pid + formatters_.push_back(details::make_unique<details::pid_formatter>(padding)); + break; + +#ifdef SPDLOG_ENABLE_MESSAGE_COUNTER + case ('i'): + formatters_.push_back(details::make_unique<details::i_formatter>(padding)); + break; +#endif + case ('^'): // color range start + formatters_.push_back(details::make_unique<details::color_start_formatter>(padding)); + break; + + case ('$'): // color range end + formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding)); + break; + + case ('@'): // source location (filename:filenumber) + formatters_.push_back(details::make_unique<details::source_location_formatter>(padding)); + break; + + case ('s'): // source filename + formatters_.push_back(details::make_unique<details::source_filename_formatter>(padding)); + break; + + case ('#'): // source line number + formatters_.push_back(details::make_unique<details::source_linenum_formatter>(padding)); + break; + + case ('!'): // source funcname + formatters_.push_back(details::make_unique<details::source_funcname_formatter>(padding)); + break; + + case ('%'): // % char + formatters_.push_back(details::make_unique<details::ch_formatter>('%')); + break; + + default: // Unknown flag appears as is + auto unknown_flag = details::make_unique<details::aggregate_formatter>(); + unknown_flag->add_ch('%'); + unknown_flag->add_ch(flag); + formatters_.push_back((std::move(unknown_flag))); + break; + } + } + + // Extract given pad spec (e.g. %8X) + // Advance the given it pass the end of the padding spec found (if any) + // Return padding. + details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end) + { + using details::padding_info; + using details::scoped_pad; + const size_t max_width = 128; + if (it == end) + { + return padding_info{}; + } + + padding_info::pad_side side; + switch (*it) + { + case '-': + side = padding_info::right; + ++it; + break; + case '=': + side = padding_info::center; + ++it; + break; + default: + side = details::padding_info::left; + break; + } + + if (it == end || !std::isdigit(static_cast<unsigned char>(*it))) + { + return padding_info{0, side}; + } + + auto width = static_cast<size_t>(*it - '0'); + for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it) + { + auto digit = static_cast<size_t>(*it - '0'); + width = width * 10 + digit; + } + return details::padding_info{std::min<size_t>(width, max_width), side}; + } + + void 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) + { + handle_flag_(*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)); + } + } +}; +} // namespace spdlog |