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 size_t line_length = end - start;
145 if (_options.pattern_suffix !=
'\n')
151 formatted_log_msg = _format(timestamp, thread_id, thread_name, process_id, logger,
152 log_level_description, log_level_short_code, log_statement_metadata,
153 named_args, std::string_view(log_msg.data() + start, line_length));
162 "log_msg should not be empty, already checked earlier in PatternFormatter::format()");
163 size_t log_message_size = log_msg.size();
165 if (_options.pattern_suffix ==
'\n' && log_msg[log_msg.size() - 1] ==
'\n')
168 log_message_size = log_msg.size() - 1;
171 formatted_log_msg = _format(timestamp, thread_id, thread_name, process_id, logger,
172 log_level_description, log_level_short_code, log_statement_metadata,
173 named_args, std::string_view{log_msg.data(), log_message_size});
176 return formatted_log_msg;
184 QUILL_NODISCARD
static std::string_view _process_source_location_path(std::string_view source_location,
185 std::string
const& strip_prefix,
186 bool remove_relative_paths)
188 std::string_view result = source_location;
191 if (remove_relative_paths)
195 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 196 static constexpr std::string_view relative_path =
"..\\";
198 static constexpr std::string_view relative_path =
"../";
201 if (
size_t n = result.rfind(relative_path); n != std::string_view::npos)
203 result = result.substr(n + relative_path.size());
208 if (!strip_prefix.empty())
211 size_t prefix_pos = result.rfind(strip_prefix);
213 if (prefix_pos != std::string_view::npos)
215 size_t after_prefix_pos = prefix_pos + strip_prefix.size();
219 if (after_prefix_pos < result.length() && result[after_prefix_pos] == detail::PATH_PREFERRED_SEPARATOR)
224 return result.substr(after_prefix_pos);
237 using namespace fmtquill::literals;
238 std::tie(_fmt_format, _order_index) = _generate_fmt_format_string(
239 _is_set_in_pattern, _options.format_pattern,
"time"_a =
"",
"file_name"_a =
"",
240 "caller_function"_a =
"",
"log_level"_a =
"",
"log_level_short_code"_a =
"",
241 "line_number"_a =
"",
"logger"_a =
"",
"full_path"_a =
"",
"thread_id"_a =
"",
242 "thread_name"_a =
"",
"process_id"_a =
"",
"source_location"_a =
"",
243 "short_source_location"_a =
"",
"message"_a =
"",
"tags"_a =
"",
"named_args"_a =
"");
245 _set_arg<Attribute::Time>(std::string_view(
"time"));
246 _set_arg<Attribute::FileName>(std::string_view(
"file_name"));
247 _set_arg<Attribute::CallerFunction>(std::string_view(
"caller_function"));
248 _set_arg<Attribute::LogLevel>(std::string_view(
"log_level"));
249 _set_arg<Attribute::LogLevelShortCode>(std::string_view(
"log_level_short_code"));
250 _set_arg<Attribute::LineNumber>(
"line_number");
251 _set_arg<Attribute::Logger>(std::string_view(
"logger"));
252 _set_arg<Attribute::FullPath>(std::string_view(
"full_path"));
253 _set_arg<Attribute::ThreadId>(std::string_view(
"thread_id"));
254 _set_arg<Attribute::ThreadName>(std::string_view(
"thread_name"));
255 _set_arg<Attribute::ProcessId>(std::string_view(
"process_id"));
256 _set_arg<Attribute::SourceLocation>(
"source_location");
257 _set_arg<Attribute::ShortSourceLocation>(
"short_source_location");
258 _set_arg<Attribute::Message>(std::string_view(
"message"));
259 _set_arg<Attribute::Tags>(std::string_view(
"tags"));
260 _set_arg<Attribute::NamedArgs>(std::string_view(
"named_args"));
264 template <
size_t I,
typename T>
265 void _set_arg(T
const& arg)
267 _args[_order_index[I]] = arg;
270 template <
size_t I,
typename T>
271 void _set_arg_val(T
const& arg)
273 fmtquill::detail::value<fmtquill::format_context>& value_ =
274 *(
reinterpret_cast<fmtquill::detail::value<fmtquill::format_context>*
>(
275 std::addressof(_args[_order_index[I]])));
277 value_ = fmtquill::detail::value<fmtquill::format_context>(arg);
281 PatternFormatter::Attribute
static _attribute_from_string(std::string
const& attribute_name)
284 std::unordered_map<std::string, PatternFormatter::Attribute>
const attr_map = {
285 {
"time", PatternFormatter::Attribute::Time},
286 {
"file_name", PatternFormatter::Attribute::FileName},
287 {
"caller_function", PatternFormatter::Attribute::CallerFunction},
288 {
"log_level", PatternFormatter::Attribute::LogLevel},
289 {
"log_level_short_code", PatternFormatter::Attribute::LogLevelShortCode},
290 {
"line_number", PatternFormatter::Attribute::LineNumber},
291 {
"logger", PatternFormatter::Attribute::Logger},
292 {
"full_path", PatternFormatter::Attribute::FullPath},
293 {
"thread_id", PatternFormatter::Attribute::ThreadId},
294 {
"thread_name", PatternFormatter::Attribute::ThreadName},
295 {
"process_id", PatternFormatter::Attribute::ProcessId},
296 {
"source_location", PatternFormatter::Attribute::SourceLocation},
297 {
"short_source_location", PatternFormatter::Attribute::ShortSourceLocation},
298 {
"message", PatternFormatter::Attribute::Message},
299 {
"tags", PatternFormatter::Attribute::Tags},
300 {
"named_args", PatternFormatter::Attribute::NamedArgs}};
302 auto const search = attr_map.find(attribute_name);
304 if (QUILL_UNLIKELY(search == attr_map.cend()))
307 std::string{
"Attribute enum value does not exist for attribute with name " + attribute_name}});
310 return search->second;
314 template <
size_t,
size_t>
315 constexpr
void _store_named_args(std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS>&)
320 template <
size_t Idx,
size_t NamedIdx,
typename Arg,
typename... Args>
321 constexpr
void _store_named_args(
322 std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS>& named_args_store,
323 Arg
const& arg, Args
const&... args)
325 named_args_store[NamedIdx] = {arg.name, Idx};
326 _store_named_args<Idx + 1, NamedIdx + 1>(named_args_store, args...);
355 template <
typename... Args>
356 QUILL_NODISCARD std::pair<std::string, std::array<size_t, PatternFormatter::Attribute::ATTR_NR_ITEMS>> _generate_fmt_format_string(
357 std::bitset<PatternFormatter::Attribute::ATTR_NR_ITEMS>& is_set_in_pattern, std::string pattern,
361 static_assert(PatternFormatter::Attribute::ATTR_NR_ITEMS ==
sizeof...(Args));
365 pattern += _options.pattern_suffix;
368 std::array<size_t, PatternFormatter::Attribute::ATTR_NR_ITEMS> order_index{};
369 order_index.fill(PatternFormatter::Attribute::ATTR_NR_ITEMS - 1);
371 std::array<fmtquill::detail::named_arg_info<char>, PatternFormatter::Attribute::ATTR_NR_ITEMS> named_args{};
372 _store_named_args<0, 0>(named_args, args...);
376 size_t arg_identifier_pos = pattern.find_first_of(
'%');
377 while (arg_identifier_pos != std::string::npos)
379 if (
size_t const open_paren_pos = pattern.find_first_of(
'(', arg_identifier_pos);
380 open_paren_pos != std::string::npos && (open_paren_pos - arg_identifier_pos) == 1)
383 size_t const closed_paren_pos = pattern.find_first_of(
')', open_paren_pos);
385 if (closed_paren_pos == std::string::npos)
387 QUILL_THROW(
QuillError{
"Invalid format pattern"});
391 std::string attr = pattern.substr(arg_identifier_pos, (closed_paren_pos + 1) - arg_identifier_pos);
394 size_t const pos = attr.find(
':');
395 std::string attr_name;
397 if (pos != std::string::npos)
404 std::string custom_format_specifier = attr.substr(pos);
405 custom_format_specifier.pop_back();
410 value += custom_format_specifier;
414 pattern.replace(arg_identifier_pos, attr.length(), value);
418 attr_name = attr.substr(2, pos - 2);
423 pattern.replace(arg_identifier_pos, attr.length(),
"{}");
429 attr_name = attr.substr(2, attr.size());
435 for (
size_t i = 0; i < PatternFormatter::Attribute::ATTR_NR_ITEMS; ++i)
437 if (named_args[i].name == attr_name)
439 id = named_args[i].id;
446 QUILL_THROW(
QuillError{
"Invalid format pattern, attribute with name \"" + attr_name +
"\" is invalid"});
449 order_index[
static_cast<size_t>(id)] = arg_idx++;
452 PatternFormatter::Attribute
const attr_enum_value = _attribute_from_string(attr_name);
453 is_set_in_pattern.set(attr_enum_value);
456 arg_identifier_pos = pattern.find_first_of(
'%');
461 arg_identifier_pos = pattern.find_first_of(
'%', arg_identifier_pos + 1);
465 return std::make_pair(pattern, order_index);
469 QUILL_ATTRIBUTE_HOT std::string_view _format(
470 uint64_t timestamp, std::string_view thread_id, std::string_view thread_name,
471 std::string_view process_id, std::string_view logger, std::string_view log_level_description,
472 std::string_view log_level_short_code,
MacroMetadata const& log_statement_metadata,
473 std::vector<std::pair<std::string, std::string>>
const* named_args, std::string_view log_msg)
475 if (_is_set_in_pattern[Attribute::Time])
477 _set_arg_val<Attribute::Time>(_timestamp_formatter.format_timestamp(std::chrono::nanoseconds{timestamp}));
480 if (_is_set_in_pattern[Attribute::FileName])
482 _set_arg_val<Attribute::FileName>(log_statement_metadata.file_name());
485 if (_is_set_in_pattern[Attribute::CallerFunction])
487 std::string_view
const function_name = _options.process_function_name
488 ? _options.process_function_name(log_statement_metadata.caller_function())
489 : std::string_view{log_statement_metadata.caller_function()};
491 _set_arg_val<Attribute::CallerFunction>(function_name);
494 if (_is_set_in_pattern[Attribute::LogLevel])
496 _set_arg_val<Attribute::LogLevel>(log_level_description);
499 if (_is_set_in_pattern[Attribute::LogLevelShortCode])
501 _set_arg_val<Attribute::LogLevelShortCode>(log_level_short_code);
504 if (_is_set_in_pattern[Attribute::LineNumber])
506 _set_arg_val<Attribute::LineNumber>(log_statement_metadata.line());
509 if (_is_set_in_pattern[Attribute::Logger])
511 _set_arg_val<Attribute::Logger>(logger);
514 if (_is_set_in_pattern[Attribute::FullPath])
516 _set_arg_val<Attribute::FullPath>(log_statement_metadata.full_path());
519 if (_is_set_in_pattern[Attribute::ThreadId])
521 _set_arg_val<Attribute::ThreadId>(thread_id);
524 if (_is_set_in_pattern[Attribute::ThreadName])
526 _set_arg_val<Attribute::ThreadName>(thread_name);
529 if (_is_set_in_pattern[Attribute::ProcessId])
531 _set_arg_val<Attribute::ProcessId>(process_id);
534 if (_is_set_in_pattern[Attribute::SourceLocation])
536 _set_arg_val<Attribute::SourceLocation>(_process_source_location_path(
537 log_statement_metadata.source_location(), _options.source_location_path_strip_prefix,
538 _options.source_location_remove_relative_paths));
542 if (_is_set_in_pattern[Attribute::ShortSourceLocation])
544 _set_arg_val<Attribute::ShortSourceLocation>(log_statement_metadata.short_source_location());
547 if (_is_set_in_pattern[Attribute::NamedArgs])
549 _formatted_named_args_buffer.clear();
553 for (
size_t i = 0; i < named_args->size(); ++i)
555 _formatted_named_args_buffer.append((*named_args)[i].first);
556 _formatted_named_args_buffer.append(std::string_view{
": "});
557 _formatted_named_args_buffer.append((*named_args)[i].second);
559 if (i != named_args->size() - 1)
561 _formatted_named_args_buffer.append(std::string_view{
", "});
566 _set_arg_val<Attribute::NamedArgs>(
567 std::string_view{_formatted_named_args_buffer.data(), _formatted_named_args_buffer.size()});
570 if (_is_set_in_pattern[Attribute::Tags])
572 if (log_statement_metadata.tags())
574 _set_arg_val<Attribute::Tags>(std::string_view{log_statement_metadata.tags()});
578 _set_arg_val<Attribute::Tags>(std::string_view{});
582 _set_arg_val<Attribute::Message>(log_msg);
584 fmtquill::vformat_to(std::back_inserter(_formatted_log_message_buffer), _fmt_format,
585 fmtquill::basic_format_args(_args.data(),
static_cast<int>(_args.size())));
587 return std::string_view{_formatted_log_message_buffer.data(), _formatted_log_message_buffer.size()};
592 std::string _fmt_format;
595 std::array<size_t, Attribute::ATTR_NR_ITEMS> _order_index{};
596 std::array<fmtquill::basic_format_arg<fmtquill::format_context>, Attribute::ATTR_NR_ITEMS> _args{};
597 std::bitset<Attribute::ATTR_NR_ITEMS> _is_set_in_pattern;
604 fmtquill::basic_memory_buffer<char, 512> _formatted_log_message_buffer;
605 fmtquill::basic_memory_buffer<char, 512> _formatted_named_args_buffer;
Definition: UserDefinedDirectFormatFuzzer.cpp:81
custom exception
Definition: QuillError.h:45
Definition: SourceLocation.h:40