9 #include "quill/backend/StringFromTime.h" 10 #include "quill/bundled/fmt/format.h" 11 #include "quill/core/Attributes.h" 12 #include "quill/core/Common.h" 13 #include "quill/core/QuillError.h" 21 #include <string_view> 41 enum AdditionalSpecifier : uint8_t
51 explicit TimestampFormatter(std::string time_format, Timezone timestamp_timezone = Timezone::LocalTime)
52 : _time_format(std::move(time_format)), _timestamp_timezone(timestamp_timezone)
55 _timestamp_timezone == Timezone::LocalTime || _timestamp_timezone == Timezone::GmtTime,
56 "Invalid timezone type in TimestampFormatter constructor, must be LocalTime or GmtTime");
59 size_t specifier_begin{std::string::npos};
63 if (
size_t const search_qms = StringFromTime::_find_unescaped_modifier(
64 _time_format, specifier_name[AdditionalSpecifier::Qms]);
65 search_qms != std::string::npos)
67 _additional_format_specifier = AdditionalSpecifier::Qms;
68 specifier_begin = search_qms;
71 if (
size_t const search_qus = StringFromTime::_find_unescaped_modifier(
72 _time_format, specifier_name[AdditionalSpecifier::Qus]);
73 search_qus != std::string::npos)
75 if (specifier_begin != std::string::npos)
77 QUILL_THROW(
QuillError{
"format specifiers %Qms, %Qus and %Qns are mutually exclusive"});
80 _additional_format_specifier = AdditionalSpecifier::Qus;
81 specifier_begin = search_qus;
84 if (
size_t const search_qns = StringFromTime::_find_unescaped_modifier(
85 _time_format, specifier_name[AdditionalSpecifier::Qns]);
86 search_qns != std::string::npos)
88 if (specifier_begin != std::string::npos)
90 QUILL_THROW(
QuillError{
"format specifiers %Qms, %Qus and %Qns are mutually exclusive"});
93 _additional_format_specifier = AdditionalSpecifier::Qns;
94 specifier_begin = search_qns;
97 if (specifier_begin == std::string::npos)
100 QUILL_ASSERT(_additional_format_specifier == AdditionalSpecifier::None,
101 "Unexpected specifier state in TimestampFormatter constructor, should be None " 102 "when no specifier found");
103 _strftime_part_1.init(_time_format, _timestamp_timezone);
108 std::string
const format_part_1 = _time_format.substr(0, specifier_begin);
109 _strftime_part_1.init(format_part_1, _timestamp_timezone);
112 size_t const specifier_end = specifier_begin + specifier_length;
113 std::string
const format_part_2 =
114 _time_format.substr(specifier_end, _time_format.length() - specifier_end);
116 if (!format_part_2.empty())
118 _strftime_part_2.init(format_part_2, _timestamp_timezone);
119 _has_format_part_2 =
true;
125 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::string_view format_timestamp(std::chrono::nanoseconds time_since_epoch)
127 int64_t
const timestamp_ns = time_since_epoch.count();
130 int64_t
const timestamp_secs = timestamp_ns / 1
'000'000
'000; 132 // First always clear our cached string 133 _formatted_date.clear(); 135 // 1. we always format part 1 136 _formatted_date.append(_strftime_part_1.format_timestamp(timestamp_secs)); 138 // 2. We add any special ms/us/ns specifier if any 139 // Note: assumes timestamp_ns >= 0 (post-epoch). Pre-epoch timestamps are not supported. 140 auto const extracted_ns = static_cast<uint32_t>(timestamp_ns - (timestamp_secs * 1'000
'000'000));
142 if (_additional_format_specifier == AdditionalSpecifier::Qms)
144 uint32_t
const extracted_ms = extracted_ns / 1
'000'000;
147 static constexpr std::string_view zeros{
"000"};
148 _formatted_date.append(zeros);
150 _write_fractional_seconds(extracted_ms);
152 else if (_additional_format_specifier == AdditionalSpecifier::Qus)
154 uint32_t
const extracted_us = extracted_ns / 1
'000; 156 // Add as many zeros as the extracted_fractional_seconds_length 157 static constexpr std::string_view zeros{"000000"}; 158 _formatted_date.append(zeros); 160 _write_fractional_seconds(extracted_us); 162 else if (_additional_format_specifier == AdditionalSpecifier::Qns) 164 // Add as many zeros as the extracted_fractional_seconds_length 165 static constexpr std::string_view zeros{"000000000"}; 166 _formatted_date.append(zeros); 168 _write_fractional_seconds(extracted_ns); 171 // 3. format part 2 after fractional seconds - if any 172 if (_has_format_part_2) 174 _formatted_date.append(_strftime_part_2.format_timestamp(timestamp_secs)); 177 return std::string_view{_formatted_date.data(), _formatted_date.size()}; 181 QUILL_NODISCARD std::string const& time_format() const noexcept { return _time_format; } 184 QUILL_NODISCARD Timezone timestamp_timezone() const noexcept { return _timestamp_timezone; } 188 void _write_fractional_seconds(uint32_t extracted_fractional_seconds) 191 switch (_additional_format_specifier) 193 case AdditionalSpecifier::Qms: 196 case AdditionalSpecifier::Qus: 199 case AdditionalSpecifier::Qns: 206 char* write_pos = _formatted_date.data() + _formatted_date.size(); 207 for (uint32_t i = 0; i < digits; ++i) 209 *--write_pos = static_cast<char>('0
' + (extracted_fractional_seconds % 10)); 210 extracted_fractional_seconds /= 10; 216 static constexpr std::array<char const*, 4> specifier_name{"", "%Qms", "%Qus", "%Qns"}; 219 static constexpr size_t specifier_length = 4u; 221 std::string _time_format; 222 fmtquill::basic_memory_buffer<char, 32> _formatted_date; 225 StringFromTime _strftime_part_1; 226 StringFromTime _strftime_part_2; 229 Timezone _timestamp_timezone; 232 AdditionalSpecifier _additional_format_specifier{AdditionalSpecifier::None}; 233 bool _has_format_part_2{false}; 236 } // namespace detail
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:28
custom exception
Definition: QuillError.h:47