9 #include "quill/backend/TimestampFormatter.h" 10 #include "quill/bundled/fmt/base.h" 11 #include "quill/bundled/fmt/format.h" 12 #include "quill/core/Attributes.h" 13 #include "quill/core/Common.h" 14 #include "quill/core/MacroMetadata.h" 15 #include "quill/core/PatternFormatterOptions.h" 16 #include "quill/core/QuillError.h" 25 #include <string_view> 27 #include <unordered_map> 48 enum Attribute : uint8_t
80 : _options(
std::move(options)),
81 _timestamp_formatter(_options.timestamp_pattern, _options.timestamp_timezone)
97 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::string_view format(
98 uint64_t timestamp, std::string_view thread_id, std::string_view thread_name,
99 std::string_view process_id, std::string_view logger, std::string_view log_level_description,
100 std::string_view log_level_short_code,
MacroMetadata const& log_statement_metadata,
101 std::vector<std::pair<std::string, std::string>>
const* named_args, std::string_view log_msg)
103 if (_options.format_pattern.empty())
108 return std::string_view{};
112 _formatted_log_message_buffer.clear();
114 if (QUILL_UNLIKELY(log_msg.empty()))
117 return _format(timestamp, thread_id, thread_name, process_id, logger, log_level_description,
118 log_level_short_code, log_statement_metadata, named_args, log_msg);
121 std::string_view formatted_log_msg;
124 if (_options.add_metadata_to_multi_line_logs && (!named_args || named_args->empty()))
129 while (start < log_msg.size())
131 size_t const end = log_msg.find_first_of(
'\n', start);
133 if (end == std::string_view::npos)
137 _format(timestamp, thread_id, thread_name, process_id, logger, log_level_description,
138 log_level_short_code, log_statement_metadata, named_args,
139 std::string_view(log_msg.data() + start, log_msg.size() - start));
144 formatted_log_msg = _format(timestamp, thread_id, thread_name, process_id, logger, log_level_description,
145 log_level_short_code, log_statement_metadata, named_args, std::string_view(log_msg.data() + start, end - start));
155 assert(!log_msg.empty() &&
"Already checked non empty message earlier");
156 size_t const log_message_size =
157 (log_msg[log_msg.size() - 1] ==
'\n') ? log_msg.size() - 1 : log_msg.size();
159 formatted_log_msg = _format(timestamp, thread_id, thread_name, process_id, logger, log_level_description,
160 log_level_short_code, log_statement_metadata, named_args, std::string_view{log_msg.data(), log_message_size});
163 return formatted_log_msg;
171 QUILL_NODISCARD
static std::string_view _process_source_location_path(std::string_view source_location,
172 std::string
const& strip_prefix,
173 bool remove_relative_paths)
175 std::string_view result = source_location;
178 if (remove_relative_paths)
182 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 183 static constexpr std::string_view relative_path =
"..\\";
185 static constexpr std::string_view relative_path =
"../";
188 if (
size_t n = result.rfind(relative_path); n != std::string_view::npos)
190 result = result.substr(n + relative_path.size());
195 if (!strip_prefix.empty())
198 size_t prefix_pos = result.rfind(strip_prefix);
200 if (prefix_pos != std::string_view::npos)
202 size_t after_prefix_pos = prefix_pos + strip_prefix.size();
206 if (after_prefix_pos < result.length() && result[after_prefix_pos] == detail::PATH_PREFERRED_SEPARATOR)
211 return result.substr(after_prefix_pos);
224 using namespace fmtquill::literals;
225 std::tie(_fmt_format, _order_index) = _generate_fmt_format_string(
226 _is_set_in_pattern, _options.format_pattern,
"time"_a =
"",
"file_name"_a =
"",
227 "caller_function"_a =
"",
"log_level"_a =
"",
"log_level_short_code"_a =
"",
228 "line_number"_a =
"",
"logger"_a =
"",
"full_path"_a =
"",
"thread_id"_a =
"",
229 "thread_name"_a =
"",
"process_id"_a =
"",
"source_location"_a =
"",
230 "short_source_location"_a =
"",
"message"_a =
"",
"tags"_a =
"",
"named_args"_a =
"");
232 _set_arg<Attribute::Time>(std::string_view(
"time"));
233 _set_arg<Attribute::FileName>(std::string_view(
"file_name"));
234 _set_arg<Attribute::CallerFunction>(std::string_view(
"caller_function"));
235 _set_arg<Attribute::LogLevel>(std::string_view(
"log_level"));
236 _set_arg<Attribute::LogLevelShortCode>(std::string_view(
"log_level_short_code"));
237 _set_arg<Attribute::LineNumber>(
"line_number");
238 _set_arg<Attribute::Logger>(std::string_view(
"logger"));
239 _set_arg<Attribute::FullPath>(std::string_view(
"full_path"));
240 _set_arg<Attribute::ThreadId>(std::string_view(
"thread_id"));
241 _set_arg<Attribute::ThreadName>(std::string_view(
"thread_name"));
242 _set_arg<Attribute::ProcessId>(std::string_view(
"process_id"));
243 _set_arg<Attribute::SourceLocation>(
"source_location");
244 _set_arg<Attribute::ShortSourceLocation>(
"short_source_location");
245 _set_arg<Attribute::Message>(std::string_view(
"message"));
246 _set_arg<Attribute::Tags>(std::string_view(
"tags"));
247 _set_arg<Attribute::NamedArgs>(std::string_view(
"named_args"));
251 template <
size_t I,
typename T>
252 void _set_arg(T
const& arg)
254 _args[_order_index[I]] = arg;
257 template <
size_t I,
typename T>
258 void _set_arg_val(T
const& arg)
260 fmtquill::detail::value<fmtquill::format_context>& value_ =
261 *(
reinterpret_cast<fmtquill::detail::value<fmtquill::format_context>*
>(
262 std::addressof(_args[_order_index[I]])));
264 value_ = fmtquill::detail::value<fmtquill::format_context>(arg);
268 PatternFormatter::Attribute
static _attribute_from_string(std::string
const& attribute_name)
271 std::unordered_map<std::string, PatternFormatter::Attribute>
const attr_map = {
272 {
"time", PatternFormatter::Attribute::Time},
273 {
"file_name", PatternFormatter::Attribute::FileName},
274 {
"caller_function", PatternFormatter::Attribute::CallerFunction},
275 {
"log_level", PatternFormatter::Attribute::LogLevel},
276 {
"log_level_short_code", PatternFormatter::Attribute::LogLevelShortCode},
277 {
"line_number", PatternFormatter::Attribute::LineNumber},
278 {
"logger", PatternFormatter::Attribute::Logger},
279 {
"full_path", PatternFormatter::Attribute::FullPath},
280 {
"thread_id", PatternFormatter::Attribute::ThreadId},
281 {
"thread_name", PatternFormatter::Attribute::ThreadName},
282 {
"process_id", PatternFormatter::Attribute::ProcessId},
283 {
"source_location", PatternFormatter::Attribute::SourceLocation},
284 {
"short_source_location", PatternFormatter::Attribute::ShortSourceLocation},
285 {
"message", PatternFormatter::Attribute::Message},
286 {
"tags", PatternFormatter::Attribute::Tags},
287 {
"named_args", PatternFormatter::Attribute::NamedArgs}};
289 auto const search = attr_map.find(attribute_name);
291 if (QUILL_UNLIKELY(search == attr_map.cend()))
294 std::string{
"Attribute enum value does not exist for attribute with name " + attribute_name}});
297 return search->second;
301 template <
size_t,
size_t>
302 constexpr
void _store_named_args(std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS>&)
307 template <
size_t Idx,
size_t NamedIdx,
typename Arg,
typename... Args>
308 constexpr
void _store_named_args(
309 std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS>& named_args_store,
310 Arg
const& arg, Args
const&... args)
312 named_args_store[NamedIdx] = {arg.name, Idx};
313 _store_named_args<Idx + 1, NamedIdx + 1>(named_args_store, args...);
342 template <
typename... Args>
343 QUILL_NODISCARD std::pair<std::string, std::array<size_t, PatternFormatter::Attribute::ATTR_NR_ITEMS>> _generate_fmt_format_string(
344 std::bitset<PatternFormatter::Attribute::ATTR_NR_ITEMS>& is_set_in_pattern, std::string pattern,
348 static_assert(PatternFormatter::Attribute::ATTR_NR_ITEMS ==
sizeof...(Args));
352 std::array<size_t, PatternFormatter::Attribute::ATTR_NR_ITEMS> order_index{};
353 order_index.fill(PatternFormatter::Attribute::ATTR_NR_ITEMS - 1);
355 std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS> named_args{};
356 _store_named_args<0, 0>(named_args, args...);
360 size_t arg_identifier_pos = pattern.find_first_of(
'%');
361 while (arg_identifier_pos != std::string::npos)
363 if (
size_t const open_paren_pos = pattern.find_first_of(
'(', arg_identifier_pos);
364 open_paren_pos != std::string::npos && (open_paren_pos - arg_identifier_pos) == 1)
367 size_t const closed_paren_pos = pattern.find_first_of(
')', open_paren_pos);
369 if (closed_paren_pos == std::string::npos)
371 QUILL_THROW(
QuillError{
"Invalid format pattern"});
375 std::string attr = pattern.substr(arg_identifier_pos, (closed_paren_pos + 1) - arg_identifier_pos);
378 size_t const pos = attr.find(
':');
379 std::string attr_name;
381 if (pos != std::string::npos)
388 std::string custom_format_specifier = attr.substr(pos);
389 custom_format_specifier.pop_back();
394 value += custom_format_specifier;
398 pattern.replace(arg_identifier_pos, attr.length(), value);
402 attr_name = attr.substr(2, pos - 2);
407 pattern.replace(arg_identifier_pos, attr.length(),
"{}");
413 attr_name = attr.substr(2, attr.size());
419 for (
size_t i = 0; i < PatternFormatter::Attribute::ATTR_NR_ITEMS; ++i)
421 if (named_args[i].name == attr_name)
423 id = named_args[i].id;
430 QUILL_THROW(
QuillError{
"Invalid format pattern, attribute with name \"" + attr_name +
"\" is invalid"});
433 order_index[
static_cast<size_t>(id)] = arg_idx++;
436 PatternFormatter::Attribute
const attr_enum_value = _attribute_from_string(attr_name);
437 is_set_in_pattern.set(attr_enum_value);
440 arg_identifier_pos = pattern.find_first_of(
'%');
445 arg_identifier_pos = pattern.find_first_of(
'%', arg_identifier_pos + 1);
449 return std::make_pair(pattern, order_index);
453 QUILL_ATTRIBUTE_HOT std::string_view _format(
454 uint64_t timestamp, std::string_view thread_id, std::string_view thread_name,
455 std::string_view process_id, std::string_view logger, std::string_view log_level_description,
456 std::string_view log_level_short_code,
MacroMetadata const& log_statement_metadata,
457 std::vector<std::pair<std::string, std::string>>
const* named_args, std::string_view log_msg)
459 if (_is_set_in_pattern[Attribute::Time])
461 _set_arg_val<Attribute::Time>(_timestamp_formatter.format_timestamp(std::chrono::nanoseconds{timestamp}));
464 if (_is_set_in_pattern[Attribute::FileName])
466 _set_arg_val<Attribute::FileName>(log_statement_metadata.file_name());
469 if (_is_set_in_pattern[Attribute::CallerFunction])
471 std::string_view
const function_name = _options.process_function_name
472 ? _options.process_function_name(log_statement_metadata.caller_function())
473 : std::string_view{log_statement_metadata.caller_function()};
475 _set_arg_val<Attribute::CallerFunction>(function_name);
478 if (_is_set_in_pattern[Attribute::LogLevel])
480 _set_arg_val<Attribute::LogLevel>(log_level_description);
483 if (_is_set_in_pattern[Attribute::LogLevelShortCode])
485 _set_arg_val<Attribute::LogLevelShortCode>(log_level_short_code);
488 if (_is_set_in_pattern[Attribute::LineNumber])
490 _set_arg_val<Attribute::LineNumber>(log_statement_metadata.line());
493 if (_is_set_in_pattern[Attribute::Logger])
495 _set_arg_val<Attribute::Logger>(logger);
498 if (_is_set_in_pattern[Attribute::FullPath])
500 _set_arg_val<Attribute::FullPath>(log_statement_metadata.full_path());
503 if (_is_set_in_pattern[Attribute::ThreadId])
505 _set_arg_val<Attribute::ThreadId>(thread_id);
508 if (_is_set_in_pattern[Attribute::ThreadName])
510 _set_arg_val<Attribute::ThreadName>(thread_name);
513 if (_is_set_in_pattern[Attribute::ProcessId])
515 _set_arg_val<Attribute::ProcessId>(process_id);
518 if (_is_set_in_pattern[Attribute::SourceLocation])
520 _set_arg_val<Attribute::SourceLocation>(_process_source_location_path(
521 log_statement_metadata.source_location(), _options.source_location_path_strip_prefix,
522 _options.source_location_remove_relative_paths));
526 if (_is_set_in_pattern[Attribute::ShortSourceLocation])
528 _set_arg_val<Attribute::ShortSourceLocation>(log_statement_metadata.short_source_location());
531 if (_is_set_in_pattern[Attribute::NamedArgs])
533 _formatted_named_args_buffer.clear();
537 for (
size_t i = 0; i < named_args->size(); ++i)
539 _formatted_named_args_buffer.append((*named_args)[i].first);
540 _formatted_named_args_buffer.append(std::string_view{
": "});
541 _formatted_named_args_buffer.append((*named_args)[i].second);
543 if (i != named_args->size() - 1)
545 _formatted_named_args_buffer.append(std::string_view{
", "});
550 _set_arg_val<Attribute::NamedArgs>(
551 std::string_view{_formatted_named_args_buffer.data(), _formatted_named_args_buffer.size()});
554 if (_is_set_in_pattern[Attribute::Tags])
556 if (log_statement_metadata.tags())
558 _set_arg_val<Attribute::Tags>(std::string_view{log_statement_metadata.tags()});
562 _set_arg_val<Attribute::Tags>(std::string_view{});
566 _set_arg_val<Attribute::Message>(log_msg);
568 fmtquill::vformat_to(std::back_inserter(_formatted_log_message_buffer), _fmt_format,
569 fmtquill::basic_format_args(_args.data(),
static_cast<int>(_args.size())));
571 return std::string_view{_formatted_log_message_buffer.data(), _formatted_log_message_buffer.size()};
576 std::string _fmt_format;
579 std::array<size_t, Attribute::ATTR_NR_ITEMS> _order_index{};
580 std::array<fmtquill::basic_format_arg<fmtquill::format_context>, Attribute::ATTR_NR_ITEMS> _args{};
581 std::bitset<Attribute::ATTR_NR_ITEMS> _is_set_in_pattern;
588 fmtquill::basic_memory_buffer<char, 512> _formatted_log_message_buffer;
589 fmtquill::basic_memory_buffer<char, 512> _formatted_named_args_buffer;
custom exception
Definition: QuillError.h:45
Definition: SourceLocation.h:40