// // 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 #include #include #include #include #include #include #include #include #include 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(msg.time); if (padinfo_.enabled()) { const size_t field_size = 3; scoped_pad p(field_size, padinfo_, dest); fmt_helper::pad3(static_cast(millis.count()), dest); } else { fmt_helper::pad3(static_cast(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(msg.time); if (padinfo_.enabled()) { const size_t field_size = 6; scoped_pad p(field_size, padinfo_, dest); fmt_helper::pad6(static_cast(micros.count()), dest); } else { fmt_helper::pad6(static_cast(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(msg.time); if (padinfo_.enabled()) { const size_t field_size = 9; scoped_pad p(field_size, padinfo_, dest); fmt_helper::pad9(static_cast(ns.count()), dest); } else { fmt_helper::pad9(static_cast(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(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(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::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(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(msg.time); fmt_helper::pad3(static_cast(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 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::padding_info{})); } pattern_formatter(const pattern_formatter &other) = delete; pattern_formatter &operator=(const pattern_formatter &other) = delete; std::unique_ptr clone() const override { return details::make_unique(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(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> 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(padding)); break; case 'n': // logger name formatters_.push_back(details::make_unique(padding)); break; case 'l': // level formatters_.push_back(details::make_unique(padding)); break; case 'L': // short level formatters_.push_back(details::make_unique(padding)); break; case ('t'): // thread id formatters_.push_back(details::make_unique(padding)); break; case ('v'): // the message text formatters_.push_back(details::make_unique(padding)); break; case ('a'): // weekday formatters_.push_back(details::make_unique(padding)); break; case ('A'): // short weekday formatters_.push_back(details::make_unique(padding)); break; case ('b'): case ('h'): // month formatters_.push_back(details::make_unique(padding)); break; case ('B'): // short month formatters_.push_back(details::make_unique(padding)); break; case ('c'): // datetime formatters_.push_back(details::make_unique(padding)); break; case ('C'): // year 2 digits formatters_.push_back(details::make_unique(padding)); break; case ('Y'): // year 4 digits formatters_.push_back(details::make_unique(padding)); break; case ('D'): case ('x'): // datetime MM/DD/YY formatters_.push_back(details::make_unique(padding)); break; case ('m'): // month 1-12 formatters_.push_back(details::make_unique(padding)); break; case ('d'): // day of month 1-31 formatters_.push_back(details::make_unique(padding)); break; case ('H'): // hours 24 formatters_.push_back(details::make_unique(padding)); break; case ('I'): // hours 12 formatters_.push_back(details::make_unique(padding)); break; case ('M'): // minutes formatters_.push_back(details::make_unique(padding)); break; case ('S'): // seconds formatters_.push_back(details::make_unique(padding)); break; case ('e'): // milliseconds formatters_.push_back(details::make_unique(padding)); break; case ('f'): // microseconds formatters_.push_back(details::make_unique(padding)); break; case ('F'): // nanoseconds formatters_.push_back(details::make_unique(padding)); break; case ('E'): // seconds since epoch formatters_.push_back(details::make_unique(padding)); break; case ('p'): // am/pm formatters_.push_back(details::make_unique(padding)); break; case ('r'): // 12 hour clock 02:55:02 pm formatters_.push_back(details::make_unique(padding)); break; case ('R'): // 24-hour HH:MM time formatters_.push_back(details::make_unique(padding)); break; case ('T'): case ('X'): // ISO 8601 time format (HH:MM:SS) formatters_.push_back(details::make_unique(padding)); break; case ('z'): // timezone formatters_.push_back(details::make_unique(padding)); break; case ('P'): // pid formatters_.push_back(details::make_unique(padding)); break; #ifdef SPDLOG_ENABLE_MESSAGE_COUNTER case ('i'): formatters_.push_back(details::make_unique(padding)); break; #endif case ('^'): // color range start formatters_.push_back(details::make_unique(padding)); break; case ('$'): // color range end formatters_.push_back(details::make_unique(padding)); break; case ('@'): // source location (filename:filenumber) formatters_.push_back(details::make_unique(padding)); break; case ('s'): // source filename formatters_.push_back(details::make_unique(padding)); break; case ('#'): // source line number formatters_.push_back(details::make_unique(padding)); break; case ('!'): // source funcname formatters_.push_back(details::make_unique(padding)); break; case ('%'): // % char 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))); 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(*it))) { return padding_info{0, side}; } auto width = static_cast(*it - '0'); for (++it; it != end && std::isdigit(static_cast(*it)); ++it) { auto digit = static_cast(*it - '0'); width = width * 10 + digit; } return details::padding_info{std::min(width, max_width), side}; } void compile_pattern_(const std::string &pattern) { auto end = pattern.end(); std::unique_ptr 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(); } user_chars->add_ch(*it); } } if (user_chars) // append raw chars found so far { formatters_.push_back(std::move(user_chars)); } } }; } // namespace spdlog