10 #include "quill/backend/Utf8Conv.h" 13 #include "quill/backend/BackendOptions.h" 14 #include "quill/backend/BackendUtilities.h" 15 #include "quill/backend/BackendWorkerLock.h" 16 #include "quill/backend/BacktraceStorage.h" 17 #include "quill/backend/PatternFormatter.h" 18 #include "quill/backend/RdtscClock.h" 19 #include "quill/backend/ThreadUtilities.h" 20 #include "quill/backend/TransitEvent.h" 21 #include "quill/backend/TransitEventBuffer.h" 23 #include "quill/core/Attributes.h" 24 #include "quill/core/BoundedSPSCQueue.h" 25 #include "quill/core/ChronoTimeUtils.h" 26 #include "quill/core/Codec.h" 27 #include "quill/core/Common.h" 28 #include "quill/core/DynamicFormatArgStore.h" 29 #include "quill/core/LogLevel.h" 30 #include "quill/core/LoggerBase.h" 31 #include "quill/core/LoggerManager.h" 32 #include "quill/core/MacroMetadata.h" 33 #include "quill/core/MathUtilities.h" 34 #include "quill/core/QuillError.h" 35 #include "quill/core/SinkManager.h" 36 #include "quill/core/ThreadContextManager.h" 37 #include "quill/core/TimeUtilities.h" 38 #include "quill/core/UnboundedSPSCQueue.h" 39 #include "quill/sinks/Sink.h" 41 #include "quill/bundled/fmt/base.h" 47 #include <condition_variable> 60 #include <string_view> 62 #include <unordered_map> 94 RdtscClock const* rdtsc_clock = _rdtsc_clock.exchange(
nullptr);
99 QUILL_NODISCARD
bool is_running()
const noexcept
101 return _is_worker_running.load(std::memory_order_acquire);
112 QuillError{
"Invalid config, When TSC clock is used backend_thread_sleep_duration should " 113 "not be higher than rdtsc_resync_interval"});
116 RdtscClock const* rdtsc_clock = _rdtsc_clock.load(std::memory_order_acquire);
117 return rdtsc_clock ? rdtsc_clock->time_since_epoch_safe(rdtsc_value) : 0;
126 return _worker_thread_id.load();
135 _ensure_linker_retains_symbols();
139 _backend_worker_lock = std::make_unique<BackendWorkerLock>(_process_id);
149 if (_options.
cpu_affinity != (std::numeric_limits<uint16_t>::max)())
155 #if !defined(QUILL_NO_EXCEPTIONS) 156 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
157 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
165 #if !defined(QUILL_NO_EXCEPTIONS) 166 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
167 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
171 _is_worker_running.store(
true);
174 while (QUILL_LIKELY(_is_worker_running.load(std::memory_order_relaxed)))
177 QUILL_TRY { _poll(); }
178 #if !defined(QUILL_NO_EXCEPTIONS) 179 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
182 _options.
error_notifier(std::string{
"Caught unhandled exception."});
188 QUILL_TRY { _exit(); }
189 #if !defined(QUILL_NO_EXCEPTIONS) 190 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
193 _options.
error_notifier(std::string{
"Caught unhandled exception."});
199 _worker_thread.swap(worker);
201 while (!_is_worker_running.load(std::memory_order_seq_cst))
204 std::this_thread::sleep_for(std::chrono::microseconds{100});
211 QUILL_ATTRIBUTE_COLD
void stop() noexcept
214 if (!_is_worker_running.exchange(
false))
224 if (_worker_thread.joinable())
226 _worker_thread.join();
229 _worker_thread_id.store(0);
230 _backend_worker_lock.reset(
nullptr);
241 std::lock_guard<std::mutex> lock{_wake_up_mutex};
242 _wake_up_flag =
true;
246 _wake_up_cv.notify_one();
254 QUILL_ATTRIBUTE_COLD
static void _ensure_linker_retains_symbols()
261 std::wstring
const dummy = L
"dummy";
262 QUILL_MAYBE_UNUSED
static auto encode1 = utf8_encode(dummy);
265 QUILL_MAYBE_UNUSED
static auto encode2 =
266 utf8_encode(reinterpret_cast<std::byte const*>(dummy.data()), dummy.size());
274 QUILL_ATTRIBUTE_HOT
void _poll()
277 _update_active_thread_contexts_cache();
280 size_t const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
282 if (cached_transit_events_count != 0)
288 _process_lowest_timestamp_transit_event();
293 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
294 _process_lowest_timestamp_transit_event())
314 _resync_rdtsc_clock();
317 bool const queues_and_events_empty = _check_frontend_queues_and_cached_transit_events_empty();
318 if (queues_and_events_empty)
320 _cleanup_invalidated_thread_contexts();
321 _cleanup_invalidated_loggers();
322 _try_shrink_empty_transit_event_buffers();
328 std::unique_lock<std::mutex> lock{_wake_up_mutex};
331 _wake_up_cv.wait_for(lock, _options.
sleep_duration, [
this] { return _wake_up_flag; });
334 _wake_up_flag =
false;
337 _resync_rdtsc_clock();
341 std::this_thread::yield();
374 "transit_events_soft_limit ({}) cannot be greater than transit_events_hard_limit " 375 "({}). Please ensure that the soft limit is less than or equal to the hard limit.",
393 QUILL_ATTRIBUTE_COLD
void _exit()
398 _check_frontend_queues_and_cached_transit_events_empty();
400 if (queues_and_events_empty)
404 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
408 uint64_t
const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
409 if (cached_transit_events_count > 0)
411 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
412 _process_lowest_timestamp_transit_event())
422 _cleanup_invalidated_thread_contexts();
423 _cleanup_invalidated_loggers();
429 QUILL_ATTRIBUTE_HOT
size_t _populate_transit_events_from_frontend_queues()
434 : std::numeric_limits<uint64_t>::max();
436 size_t cached_transit_events_count{0};
438 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
440 assert(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type());
442 if (thread_context->has_unbounded_queue_type())
444 cached_transit_events_count += _read_and_decode_frontend_queue(
445 thread_context->get_spsc_queue_union().unbounded_spsc_queue, thread_context, ts_now);
447 else if (thread_context->has_bounded_queue_type())
449 cached_transit_events_count += _read_and_decode_frontend_queue(
450 thread_context->get_spsc_queue_union().bounded_spsc_queue, thread_context, ts_now);
454 return cached_transit_events_count;
464 template <
typename TFrontendQueue>
465 QUILL_ATTRIBUTE_HOT
size_t _read_and_decode_frontend_queue(TFrontendQueue& frontend_queue,
470 size_t const queue_capacity = frontend_queue.capacity();
471 size_t total_bytes_read{0};
477 if constexpr (std::is_same_v<TFrontendQueue, UnboundedSPSCQueue>)
479 read_pos = _read_unbounded_frontend_queue(frontend_queue, thread_context);
483 read_pos = frontend_queue.prepare_read();
492 std::byte
const*
const read_begin = read_pos;
494 if (!_populate_transit_event_from_frontend_queue(read_pos, thread_context, ts_now))
501 assert((read_pos >= read_begin) &&
"read_buffer should be greater or equal to read_begin");
502 auto const bytes_read =
static_cast<size_t>(read_pos - read_begin);
503 frontend_queue.finish_read(bytes_read);
504 total_bytes_read += bytes_read;
507 }
while ((total_bytes_read < queue_capacity) &&
510 if (total_bytes_read != 0)
515 frontend_queue.commit_read();
518 return thread_context->_transit_event_buffer->size();
522 QUILL_ATTRIBUTE_HOT
bool _populate_transit_event_from_frontend_queue(std::byte*& read_pos,
526 assert(thread_context->_transit_event_buffer);
529 TransitEvent* transit_event = thread_context->_transit_event_buffer->back();
531 assert(transit_event->formatted_msg);
533 std::memcpy(&transit_event->timestamp, read_pos,
sizeof(transit_event->timestamp));
534 read_pos +=
sizeof(transit_event->timestamp);
536 std::memcpy(&transit_event->macro_metadata, read_pos,
sizeof(transit_event->macro_metadata));
537 read_pos +=
sizeof(transit_event->macro_metadata);
539 std::memcpy(&transit_event->logger_base, read_pos,
sizeof(transit_event->logger_base));
540 read_pos +=
sizeof(transit_event->logger_base);
542 if (transit_event->logger_base->_clock_source == ClockSourceType::Tsc)
547 if (QUILL_UNLIKELY(!_rdtsc_clock.load(std::memory_order_relaxed)))
552 _last_rdtsc_resync_time = std::chrono::steady_clock::now();
556 transit_event->timestamp =
557 _rdtsc_clock.load(std::memory_order_relaxed)->time_since_epoch(transit_event->timestamp);
561 if ((transit_event->logger_base->_clock_source != ClockSourceType::User) &&
562 (ts_now != std::numeric_limits<uint64_t>::max()))
569 auto count_digits = [](uint64_t number)
576 }
while (number != 0);
580 assert(count_digits(transit_event->timestamp) == count_digits(ts_now));
584 if (QUILL_UNLIKELY(transit_event->timestamp > ts_now))
597 std::memcpy(&format_args_decoder, read_pos,
sizeof(format_args_decoder));
598 read_pos +=
sizeof(format_args_decoder);
600 if ((transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy) ||
601 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy) ||
602 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy))
604 _apply_runtime_metadata(read_pos, transit_event);
608 if ((transit_event->macro_metadata->event() != MacroMetadata::Event::Flush) &&
609 (transit_event->macro_metadata->event() != MacroMetadata::Event::LoggerRemovalRequest))
611 format_args_decoder(read_pos, _format_args_store);
613 if (!transit_event->macro_metadata->has_named_args())
615 _populate_formatted_log_message(transit_event, transit_event->macro_metadata->message_format());
620 _named_args_format_template.assign(transit_event->macro_metadata->message_format());
622 if (
auto const search = _named_args_templates.find(_named_args_format_template);
623 search != std::cend(_named_args_templates))
627 auto const& [message_format, arg_names] = search->second;
629 _populate_formatted_log_message(transit_event, message_format.data());
630 _populate_formatted_named_args(transit_event, arg_names);
636 auto const [res_it, inserted] = _named_args_templates.try_emplace(
637 _named_args_format_template,
638 _process_named_args_format_message(transit_event->macro_metadata->message_format()));
640 auto const& [message_format, arg_names] = res_it->second;
645 _populate_formatted_log_message(transit_event, message_format.data());
646 _populate_formatted_named_args(transit_event, arg_names);
650 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::Flush)
654 uintptr_t flush_flag_tmp;
655 std::memcpy(&flush_flag_tmp, read_pos,
sizeof(uintptr_t));
656 transit_event->
flush_flag =
reinterpret_cast<std::atomic<bool>*
>(flush_flag_tmp);
657 read_pos +=
sizeof(uintptr_t);
662 assert(transit_event->macro_metadata->event() == MacroMetadata::Event::LoggerRemovalRequest);
664 uintptr_t logger_removal_flag_tmp;
665 std::memcpy(&logger_removal_flag_tmp, read_pos,
sizeof(uintptr_t));
666 read_pos +=
sizeof(uintptr_t);
669 _logger_removal_flags.emplace(std::string{logger_name},
670 reinterpret_cast<std::atomic<bool>*
>(logger_removal_flag_tmp));
674 thread_context->_transit_event_buffer->push_back();
683 QUILL_ATTRIBUTE_HOT
bool has_pending_events_for_caching_when_transit_event_buffer_empty() noexcept
685 _update_active_thread_contexts_cache();
687 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
689 assert(thread_context->_transit_event_buffer &&
690 "transit_event_buffer should always be valid here as we always populate it with the " 691 "_active_thread_contexts_cache");
693 if (thread_context->_transit_event_buffer->empty())
696 if (thread_context->has_unbounded_queue_type() &&
697 !thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty())
702 if (thread_context->has_bounded_queue_type() &&
703 !thread_context->get_spsc_queue_union().bounded_spsc_queue.empty())
716 QUILL_ATTRIBUTE_HOT
bool _process_lowest_timestamp_transit_event()
719 uint64_t min_ts{std::numeric_limits<uint64_t>::max()};
724 assert(tc->_transit_event_buffer &&
725 "transit_event_buffer should always be valid here as we always populate it with the " 726 "_active_thread_contexts_cache");
728 TransitEvent const* te = tc->_transit_event_buffer->front();
729 if (te && (min_ts > te->timestamp))
731 min_ts = te->timestamp;
742 TransitEvent* transit_event = thread_context->_transit_event_buffer->front();
743 assert(transit_event &&
"transit_buffer is set only when transit_event is valid");
745 std::atomic<bool>* flush_flag{
nullptr};
747 QUILL_TRY { _process_transit_event(*thread_context, *transit_event, flush_flag); }
748 #if !defined(QUILL_NO_EXCEPTIONS) 749 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
752 _options.
error_notifier(std::string{
"Caught unhandled exception."});
759 transit_event->
extra_data->named_args.clear();
760 transit_event->
extra_data->runtime_metadata.has_runtime_metadata =
false;
763 thread_context->_transit_event_buffer->pop_front();
777 _cleanup_invalidated_thread_contexts();
780 flush_flag->store(
true);
789 QUILL_ATTRIBUTE_HOT
void _process_transit_event(
ThreadContext const& thread_context,
790 TransitEvent& transit_event, std::atomic<bool>*& flush_flag)
794 if (transit_event.macro_metadata->event() == MacroMetadata::Event::Log)
796 if (transit_event.log_level() != LogLevel::Backtrace)
798 _dispatch_transit_event_to_sinks(transit_event, thread_context.thread_id(),
799 thread_context.thread_name());
806 if (QUILL_UNLIKELY(transit_event.log_level() >=
807 transit_event.logger_base->_backtrace_flush_level.load(std::memory_order_relaxed)))
809 if (transit_event.logger_base->_backtrace_storage)
811 transit_event.logger_base->_backtrace_storage->process(
812 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
813 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
819 if (transit_event.logger_base->_backtrace_storage)
825 transit_event.copy_to(transit_event_copy);
827 transit_event.logger_base->_backtrace_storage->store(
828 std::move(transit_event_copy), thread_context.thread_id(), thread_context.thread_name());
833 QuillError{
"logger->init_backtrace(...) needs to be called first before using " 834 "LOG_BACKTRACE(...)."});
838 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::InitBacktrace)
841 if (!transit_event.logger_base->_backtrace_storage)
844 transit_event.logger_base->_backtrace_storage = std::make_shared<BacktraceStorage>();
847 transit_event.logger_base->_backtrace_storage->set_capacity(static_cast<uint32_t>(std::stoul(
848 std::string{transit_event.formatted_msg->begin(), transit_event.formatted_msg->end()})));
850 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::FlushBacktrace)
852 if (transit_event.logger_base->_backtrace_storage)
855 transit_event.logger_base->_backtrace_storage->process(
856 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
857 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
860 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::Flush)
862 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
877 QUILL_ATTRIBUTE_HOT
void _dispatch_transit_event_to_sinks(
TransitEvent const& transit_event,
878 std::string_view
const& thread_id,
879 std::string_view
const& thread_name)
883 if (QUILL_UNLIKELY(!transit_event.logger_base->_pattern_formatter))
889 if (logger->_pattern_formatter &&
890 (logger->_pattern_formatter->get_options() == transit_event.logger_base->_pattern_formatter_options))
893 transit_event.logger_base->_pattern_formatter = logger->_pattern_formatter;
900 if (!transit_event.logger_base->_pattern_formatter)
903 transit_event.logger_base->_pattern_formatter =
904 std::make_shared<PatternFormatter>(transit_event.logger_base->_pattern_formatter_options);
908 assert(transit_event.logger_base->_pattern_formatter &&
909 "transit_event->logger_base->pattern_formatter should be valid here");
912 std::string_view
const log_level_description =
916 std::string_view
const log_level_short_code =
920 _write_log_statement(
921 transit_event, thread_id, thread_name, log_level_description, log_level_short_code,
922 std::string_view{transit_event.formatted_msg->data(), transit_event.formatted_msg->size()});
928 QUILL_ATTRIBUTE_HOT
void _write_log_statement(
TransitEvent const& transit_event,
929 std::string_view
const& thread_id,
930 std::string_view
const& thread_name,
931 std::string_view
const& log_level_description,
932 std::string_view
const& log_level_short_code,
933 std::string_view
const& log_message)
const 935 std::string_view default_log_statement;
938 for (
auto& sink : transit_event.logger_base->_sinks)
940 std::string_view log_to_write;
943 if (!sink->_override_pattern_formatter_options)
945 if (default_log_statement.empty())
949 default_log_statement = transit_event.logger_base->_pattern_formatter->format(
950 transit_event.timestamp, thread_id, thread_name, _process_id,
951 transit_event.logger_base->_logger_name, log_level_description, log_level_short_code,
952 *transit_event.macro_metadata, transit_event.get_named_args(), log_message);
955 log_to_write = default_log_statement;
961 if (!sink->_override_pattern_formatter)
964 sink->_override_pattern_formatter =
965 std::make_shared<PatternFormatter>(*sink->_override_pattern_formatter_options);
969 log_to_write = sink->_override_pattern_formatter->format(
970 transit_event.timestamp, thread_id, thread_name, _process_id,
971 transit_event.logger_base->_logger_name, log_level_description, log_level_short_code,
972 *transit_event.macro_metadata, transit_event.get_named_args(), log_message);
976 if (sink->apply_all_filters(transit_event.macro_metadata, transit_event.timestamp, thread_id,
977 thread_name, transit_event.logger_base->_logger_name,
978 transit_event.log_level(), log_message, log_to_write))
981 sink->write_log(transit_event.macro_metadata, transit_event.timestamp, thread_id,
982 thread_name, _process_id, transit_event.logger_base->_logger_name,
983 transit_event.log_level(), log_level_description, log_level_short_code,
984 transit_event.get_named_args(), log_message, log_to_write);
993 QUILL_ATTRIBUTE_HOT
void _check_failure_counter(std::function<
void(std::string
const&)>
const& error_notifier) noexcept
996 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
998 if (thread_context->has_bounded_queue_type())
1000 size_t const failed_messages_cnt = thread_context->get_and_reset_failure_counter();
1002 if (QUILL_UNLIKELY(failed_messages_cnt > 0))
1005 time_t now = time(
nullptr);
1008 strftime(timestamp,
sizeof(timestamp),
"%X", &local_time);
1010 if (thread_context->has_dropping_queue())
1012 error_notifier(fmtquill::format(
"{} Quill INFO: Dropped {} log messages from thread {}",
1013 timestamp, failed_messages_cnt, thread_context->thread_id()));
1015 else if (thread_context->has_blocking_queue())
1018 fmtquill::format(
"{} Quill INFO: Experienced {} blocking occurrences on thread {}",
1019 timestamp, failed_messages_cnt, thread_context->thread_id()));
1031 QUILL_ATTRIBUTE_HOT
static std::pair<std::string, std::vector<std::pair<std::string, std::string>>> _process_named_args_format_message(
1032 std::string_view fmt_template) noexcept
1036 std::string fmt_str;
1037 std::vector<std::pair<std::string, std::string>> keys;
1041 size_t open_bracket_pos = fmt_template.find_first_of(
'{');
1042 while (open_bracket_pos != std::string::npos)
1045 if (
size_t const open_bracket_2_pos = fmt_template.find_first_of(
'{', open_bracket_pos + 1);
1046 open_bracket_2_pos != std::string::npos)
1049 if ((open_bracket_2_pos - 1) == open_bracket_pos)
1051 open_bracket_pos = fmt_template.find_first_of(
'{', open_bracket_2_pos + 1);
1057 size_t close_bracket_pos = fmt_template.find_first_of(
'}', open_bracket_pos + 1);
1058 while (close_bracket_pos != std::string::npos)
1061 if (
size_t const close_bracket_2_pos = fmt_template.find_first_of(
'}', close_bracket_pos + 1);
1062 close_bracket_2_pos != std::string::npos)
1065 if ((close_bracket_2_pos - 1) == close_bracket_pos)
1067 close_bracket_pos = fmt_template.find_first_of(
'}', close_bracket_2_pos + 1);
1073 std::string_view
const text_inside_placeholders =
1074 fmt_template.substr(open_bracket_pos + 1, close_bracket_pos - (open_bracket_pos + 1));
1075 std::string_view arg_syntax;
1076 std::string_view arg_name;
1079 if (
size_t const syntax_separator = text_inside_placeholders.find(
':');
1080 syntax_separator != std::string_view::npos)
1082 arg_syntax = text_inside_placeholders.substr(
1083 syntax_separator, text_inside_placeholders.size() - syntax_separator);
1084 arg_name = text_inside_placeholders.substr(0, syntax_separator);
1088 arg_name = text_inside_placeholders;
1091 fmt_str += fmtquill::format(
1092 "{}{{{}}}", fmt_template.substr(cur_pos, open_bracket_pos - cur_pos), arg_syntax);
1093 cur_pos = close_bracket_pos + 1;
1096 keys.emplace_back(arg_name, arg_syntax);
1101 open_bracket_pos = fmt_template.find_first_of(
'{', close_bracket_pos);
1105 fmt_str += std::string{fmt_template.substr(cur_pos, fmt_template.length() - cur_pos)};
1106 return std::make_pair(fmt_str, keys);
1115 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::byte* _read_unbounded_frontend_queue(
UnboundedSPSCQueue& frontend_queue,
1118 auto const read_result = frontend_queue.
prepare_read();
1120 if (read_result.allocation)
1122 if ((read_result.new_capacity < read_result.previous_capacity) && thread_context->_transit_event_buffer)
1126 thread_context->_transit_event_buffer->request_shrink();
1133 time_t t = time(
nullptr);
1136 strftime(ts, 24,
"%X", std::addressof(p));
1141 fmtquill::format(
"{} Quill INFO: Allocated a new SPSC queue with a capacity of {} KiB " 1142 "(previously {} KiB) from thread {}",
1143 ts, (read_result.new_capacity / 1024),
1144 (read_result.previous_capacity / 1024), thread_context->thread_id()));
1148 return read_result.read_pos;
1151 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT
bool _check_frontend_queues_and_cached_transit_events_empty()
1153 _update_active_thread_contexts_cache();
1155 bool all_empty{
true};
1157 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1159 assert(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type());
1161 if (thread_context->has_unbounded_queue_type())
1163 all_empty &= thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty();
1165 else if (thread_context->has_bounded_queue_type())
1167 all_empty &= thread_context->get_spsc_queue_union().bounded_spsc_queue.empty();
1170 assert(thread_context->_transit_event_buffer &&
1171 "transit_event_buffer should always be valid here as we always populate it with the " 1172 "_active_thread_contexts_cache");
1174 all_empty &= thread_context->_transit_event_buffer->empty();
1183 QUILL_ATTRIBUTE_HOT
void _resync_rdtsc_clock()
1185 if (_rdtsc_clock.load(std::memory_order_relaxed))
1188 if (
auto const now = std::chrono::steady_clock::now();
1191 if (_rdtsc_clock.load(std::memory_order_relaxed)->resync(2500))
1193 _last_rdtsc_resync_time = now;
1200 QUILL_ATTRIBUTE_HOT
void _flush_and_run_active_sinks(
bool run_periodic_tasks, std::chrono::milliseconds sink_min_flush_interval)
1208 for (std::shared_ptr<Sink>
const& sink : logger->_sinks)
1210 Sink* logger_sink_ptr = sink.get();
1211 auto search_it = std::find_if(_active_sinks_cache.begin(), _active_sinks_cache.end(),
1212 [logger_sink_ptr](
Sink* elem)
1216 return elem == logger_sink_ptr;
1219 if (search_it == std::end(_active_sinks_cache))
1221 _active_sinks_cache.push_back(logger_sink_ptr);
1230 bool should_flush_sinks{
false};
1231 if (sink_min_flush_interval.count())
1234 if (
auto const now = std::chrono::steady_clock::now(); (now - _last_sink_flush_time) > sink_min_flush_interval)
1236 should_flush_sinks =
true;
1237 _last_sink_flush_time = now;
1243 should_flush_sinks =
true;
1246 for (
auto const& sink : _active_sinks_cache)
1250 if (should_flush_sinks)
1258 #if !defined(QUILL_NO_EXCEPTIONS) 1259 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
1260 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
1263 if (run_periodic_tasks)
1265 sink->run_periodic_tasks();
1269 _active_sinks_cache.clear();
1275 QUILL_ATTRIBUTE_HOT
void _update_active_thread_contexts_cache()
1278 if (QUILL_UNLIKELY(_thread_context_manager.new_thread_context_flag()))
1280 _active_thread_contexts_cache.clear();
1281 _thread_context_manager.for_each_thread_context(
1284 if (!thread_context->_transit_event_buffer)
1287 thread_context->_transit_event_buffer =
1288 std::make_shared<TransitEventBuffer>(_options.transit_event_buffer_initial_capacity);
1293 _active_thread_contexts_cache.push_back(thread_context);
1304 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_thread_contexts()
1306 if (!_thread_context_manager.has_invalid_thread_context())
1311 auto find_invalid_and_empty_thread_context_callback = [](
ThreadContext* thread_context)
1315 if (!thread_context->is_valid())
1317 assert(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type());
1319 assert(thread_context->_transit_event_buffer &&
1320 "transit_event_buffer should always be valid here as we always populate it with the " 1321 "_active_thread_contexts_cache");
1324 if (thread_context->has_unbounded_queue_type())
1326 return thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty() &&
1327 thread_context->_transit_event_buffer->empty();
1330 if (thread_context->has_bounded_queue_type())
1332 return thread_context->get_spsc_queue_union().bounded_spsc_queue.empty() &&
1333 thread_context->_transit_event_buffer->empty();
1341 auto found_invalid_and_empty_thread_context =
1342 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1343 find_invalid_and_empty_thread_context_callback);
1345 while (QUILL_UNLIKELY(found_invalid_and_empty_thread_context != std::end(_active_thread_contexts_cache)))
1350 _thread_context_manager.remove_shared_invalidated_thread_context(*found_invalid_and_empty_thread_context);
1353 _active_thread_contexts_cache.erase(found_invalid_and_empty_thread_context);
1356 found_invalid_and_empty_thread_context =
1357 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1358 find_invalid_and_empty_thread_context_callback);
1365 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_loggers()
1368 std::vector<std::string>
const removed_loggers = _logger_manager.cleanup_invalidated_loggers(
1373 return _check_frontend_queues_and_cached_transit_events_empty();
1376 if (!removed_loggers.empty())
1380 _sink_manager.cleanup_unused_sinks();
1382 for (
auto const& removed_logger_name : removed_loggers)
1385 auto search_it = _logger_removal_flags.find(removed_logger_name);
1386 if (search_it != _logger_removal_flags.end())
1388 search_it->second->store(
true);
1389 _logger_removal_flags.erase(search_it);
1399 QUILL_ATTRIBUTE_HOT
void _try_shrink_empty_transit_event_buffers()
1401 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1403 if (thread_context->_transit_event_buffer)
1405 thread_context->_transit_event_buffer->try_shrink();
1416 static void _format_and_split_arguments(std::vector<std::pair<std::string, std::string>>
const& orig_arg_names,
1417 std::vector<std::pair<std::string, std::string>>& named_args,
1422 std::string format_string;
1423 static constexpr std::string_view delimiter{QUILL_MAGIC_SEPARATOR};
1425 for (
size_t i = 0; i < named_args.size(); ++i)
1429 if ((i < orig_arg_names.size()) && !orig_arg_names[i].second.empty())
1432 format_string += fmtquill::format(
"{{{}}}", orig_arg_names[i].second);
1436 format_string +=
"{}";
1439 if (i < named_args.size() - 1)
1441 format_string += delimiter;
1446 std::string formatted_values_str;
1447 fmtquill::vformat_to(std::back_inserter(formatted_values_str), format_string,
1448 fmtquill::basic_format_args<fmtquill::format_context>{
1449 format_args_store.data(), format_args_store.size()});
1456 while ((end = formatted_values_str.find(delimiter, start)) != std::string::npos)
1458 if (idx < named_args.size())
1460 named_args[idx++].second = formatted_values_str.substr(start, end - start);
1462 start = end + delimiter.length();
1466 if (idx < named_args.size())
1468 named_args[idx].second = formatted_values_str.substr(start);
1478 sanitize_non_printable_chars(
named_arg.second, options);
1483 void _populate_formatted_named_args(
TransitEvent* transit_event,
1484 std::vector<std::pair<std::string, std::string>>
const& arg_names)
1486 transit_event->ensure_extra_data();
1488 auto* named_args = &transit_event->
extra_data->named_args;
1490 named_args->resize(arg_names.size());
1493 for (
size_t i = 0; i < arg_names.size(); ++i)
1495 (*named_args)[i].first = arg_names[i].first;
1498 for (
size_t i = arg_names.size(); i < static_cast<size_t>(_format_args_store.size()); ++i)
1501 named_args->push_back(std::pair<std::string, std::string>(fmtquill::format(
"_{}", i), std::string{}));
1505 QUILL_TRY { _format_and_split_arguments(arg_names, *named_args, _format_args_store, _options); }
1506 #if !defined(QUILL_NO_EXCEPTIONS) 1507 QUILL_CATCH(std::exception
const&)
1516 QUILL_ATTRIBUTE_HOT
void _populate_formatted_log_message(
TransitEvent* transit_event,
char const* message_format)
1518 transit_event->formatted_msg->clear();
1522 fmtquill::vformat_to(std::back_inserter(*transit_event->formatted_msg), message_format,
1523 fmtquill::basic_format_args<fmtquill::format_context>{
1524 _format_args_store.data(), _format_args_store.size()});
1528 sanitize_non_printable_chars(*transit_event->formatted_msg, _options);
1531 #if !defined(QUILL_NO_EXCEPTIONS) 1532 QUILL_CATCH(std::exception
const& e)
1534 transit_event->formatted_msg->clear();
1535 std::string
const error =
1536 fmtquill::format(R
"([Could not format log statement. message: "{}", location: "{}", error: "{}"])", 1537 transit_event->macro_metadata->message_format(), 1538 transit_event->macro_metadata->short_source_location(), e.what()); 1540 transit_event->formatted_msg->append(error); 1546 void _apply_runtime_metadata(std::byte*& read_pos,
TransitEvent* transit_event)
1550 char const*
function;
1553 if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy)
1560 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy)
1567 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy)
1577 QuillError{
"Unexpected event type in _apply_runtime_metadata. This should never happen."});
1585 transit_event->ensure_extra_data();
1586 transit_event->
extra_data->runtime_metadata = temp;
1589 transit_event->macro_metadata = &transit_event->
extra_data->runtime_metadata.macro_metadata;
1592 template <
typename TFormattedMsg>
1593 static void sanitize_non_printable_chars(TFormattedMsg& formatted_msg,
BackendOptions const& options)
1596 bool contains_non_printable_char{
false};
1598 for (
char c : formatted_msg)
1602 contains_non_printable_char =
true;
1607 if (contains_non_printable_char)
1610 std::string
const formatted_msg_copy = {formatted_msg.data(), formatted_msg.size()};
1611 formatted_msg.clear();
1613 for (
char c : formatted_msg_copy)
1617 formatted_msg.append(std::string{c});
1622 constexpr
char hex[] =
"0123456789ABCDEF";
1623 formatted_msg.append(std::string{
'\\'});
1624 formatted_msg.append(std::string{
'x'});
1625 formatted_msg.append(std::string{hex[(c >> 4) & 0xF]});
1626 formatted_msg.append(std::string{hex[c & 0xF]});
1633 friend class quill::ManualBackendWorker;
1635 std::unique_ptr<BackendWorkerLock> _backend_worker_lock;
1637 SinkManager& _sink_manager = SinkManager::instance();
1640 std::thread _worker_thread;
1643 std::vector<ThreadContext*> _active_thread_contexts_cache;
1644 std::vector<Sink*> _active_sinks_cache;
1645 std::unordered_map<std::string, std::pair<std::string, std::vector<std::pair<std::string, std::string>>>> _named_args_templates;
1646 std::unordered_map<std::string, std::atomic<bool>*> _logger_removal_flags;
1647 std::string _named_args_format_template;
1648 std::string _process_id;
1649 std::chrono::steady_clock::time_point _last_rdtsc_resync_time;
1650 std::chrono::steady_clock::time_point _last_sink_flush_time;
1651 std::atomic<uint32_t> _worker_thread_id{0};
1652 std::atomic<bool> _is_worker_running{
false};
1654 alignas(QUILL_CACHE_LINE_ALIGNED) std::atomic<RdtscClock*> _rdtsc_clock{
1656 alignas(QUILL_CACHE_LINE_ALIGNED) std::mutex _wake_up_mutex;
1657 std::condition_variable _wake_up_cv;
1658 bool _wake_up_flag{
false};
std::unique_ptr< ExtraData > extra_data
buffer for message
Definition: TransitEvent.h:216
A single-producer single-consumer FIFO circular buffer.
Definition: UnboundedSPSCQueue.h:37
void notify()
Wakes up the backend worker thread.
Definition: BackendWorker.h:237
Base class for sinks.
Definition: Sink.h:40
QUILL_ATTRIBUTE_COLD void run(BackendOptions const &options)
Starts the backend worker thread.
Definition: BackendWorker.h:133
bool enable_yield_when_idle
The backend employs "busy-waiting" by spinning around each frontend thread's queue.
Definition: BackendOptions.h:44
Definition: TransitEvent.h:32
~BackendWorker()
Destructor.
Definition: BackendWorker.h:89
QUILL_NODISCARD uint64_t time_since_epoch(uint64_t rdtsc_value) const
Access the rdtsc class from any thread to convert an rdtsc value to wall time.
Definition: BackendWorker.h:107
Definition: ThreadContextManager.h:210
void(*)(std::byte *&data, DynamicFormatArgStore &args_store) FormatArgsDecoder
Decode functions.
Definition: Codec.h:382
uint16_t cpu_affinity
Pins the backend to the specified CPU.
Definition: BackendOptions.h:146
std::function< void(std::string const &)> error_notifier
The backend may encounter exceptions that cannot be caught within user threads.
Definition: BackendOptions.h:162
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED std::string get_thread_name()
Returns the name of the thread.
Definition: ThreadUtilities.h:147
std::chrono::nanoseconds sleep_duration
Specifies the duration the backend sleeps if there is no remaining work to process in the queues...
Definition: BackendOptions.h:49
tm * localtime_rs(time_t const *timer, tm *buf)
Portable localtime_r or _s per operating system.
Definition: TimeUtilities.h:59
BackendWorker()
Constructor.
Definition: BackendWorker.h:78
Definition: LogFunctions.h:261
QUILL_NODISCARD bool is_valid_logger() const noexcept
Checks if the logger is valid.
Definition: LoggerBase.h:111
void for_each_logger(TCallback cb) const
For backend use only.
Definition: LoggerManager.h:109
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
typename = void for specializations with enable_if
Definition: Codec.h:142
Converts tsc ticks to nanoseconds since epoch.
Definition: RdtscClock.h:30
std::array< std::string, 11 > log_level_descriptions
Holds descriptive names for various log levels used in logging operations.
Definition: BackendOptions.h:225
size_t transit_events_hard_limit
The backend gives priority to reading messages from the frontend queues and temporarily buffers them...
Definition: BackendOptions.h:92
QUILL_ATTRIBUTE_COLD void stop() noexcept
Stops the backend worker thread.
Definition: BackendWorker.h:211
This class can be used when you want to run the backend worker on your own thread.
Definition: ManualBackendWorker.h:20
Definition: LoggerBase.h:36
custom exception
Definition: QuillError.h:45
QUILL_NODISCARD uint32_t get_backend_thread_id() const noexcept
Get the backend worker's thread id.
Definition: BackendWorker.h:124
std::array< std::string, 11 > log_level_short_codes
Short codes or identifiers for each log level.
Definition: BackendOptions.h:235
bool wait_for_queues_to_empty_before_exit
When this option is enabled and the application is terminating, the backend worker thread will not ex...
Definition: BackendOptions.h:137
size_t transit_events_soft_limit
The backend gives priority to reading messages from the frontend queues of all the hot threads and te...
Definition: BackendOptions.h:75
QUILL_NODISCARD constexpr bool is_power_of_two(uint64_t number) noexcept
Check if a number is a power of 2.
Definition: MathUtilities.h:25
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT ReadResult prepare_read()
Prepare to read from the buffer a callback used for notifications to the user.
Definition: UnboundedSPSCQueue.h:185
Definition: SinkManager.h:28
bool check_backend_singleton_instance
Enables a runtime check to detect multiple instances of the backend singleton.
Definition: BackendOptions.h:258
std::atomic< bool > * flush_flag
A unique ptr to save space as these fields not always used.
Definition: TransitEvent.h:217
std::string thread_name
The name assigned to the backend, visible during thread naming queries (e.g., pthread_getname_np) or ...
Definition: BackendOptions.h:36
Definition: BackendWorker.h:72
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED uint32_t get_thread_id() noexcept
Returns the os assigned ID of the thread.
Definition: ThreadUtilities.h:193
Definition: LoggerManager.h:34
Configuration options for the backend.
Definition: BackendOptions.h:30
std::chrono::microseconds log_timestamp_ordering_grace_period
The backend iterates through all frontend lock-free queues and pops all messages from each queue...
Definition: BackendOptions.h:124
std::function< bool(char c)> check_printable_char
This option enables a check that verifies the log message contains only printable characters before f...
Definition: BackendOptions.h:217
Definition: ThreadContextManager.h:48
std::chrono::milliseconds rdtsc_resync_interval
This option is only applicable if at least one frontend is using a Logger with ClockSourceType::Tsc.
Definition: BackendOptions.h:185
std::chrono::milliseconds sink_min_flush_interval
This option specifies the minimum time interval (in milliseconds) before the backend thread flushes t...
Definition: BackendOptions.h:202