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" 46 #include <condition_variable> 59 #include <string_view> 61 #include <unordered_map> 72 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 74 #pragma warning(disable : 4324) 99 RdtscClock const* rdtsc_clock = _rdtsc_clock.exchange(
nullptr);
104 QUILL_NODISCARD
bool is_running()
const noexcept
106 return _is_worker_running.load(std::memory_order_acquire);
117 QuillError{
"Invalid config, When TSC clock is used backend_thread_sleep_duration should " 118 "not be higher than rdtsc_resync_interval"});
121 RdtscClock const* rdtsc_clock = _rdtsc_clock.load(std::memory_order_acquire);
122 return rdtsc_clock ? rdtsc_clock->time_since_epoch_safe(rdtsc_value) : 0;
131 return _worker_thread_id.load();
140 _ensure_linker_retains_symbols();
142 _process_id = std::to_string(get_process_id());
146 _backend_worker_lock = std::make_unique<BackendWorkerLock>(_process_id);
156 if (_options.
cpu_affinity != (std::numeric_limits<uint16_t>::max)())
162 #if !defined(QUILL_NO_EXCEPTIONS) 163 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
164 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
172 #if !defined(QUILL_NO_EXCEPTIONS) 173 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
174 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
178 _is_worker_running.store(
true);
181 while (QUILL_LIKELY(_is_worker_running.load(std::memory_order_relaxed)))
184 QUILL_TRY { _poll(); }
185 #if !defined(QUILL_NO_EXCEPTIONS) 186 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
187 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
192 QUILL_TRY { _exit(); }
193 #if !defined(QUILL_NO_EXCEPTIONS) 194 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
195 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
200 _worker_thread.swap(worker);
202 while (!_is_worker_running.load(std::memory_order_seq_cst))
205 std::this_thread::sleep_for(std::chrono::microseconds{100});
212 QUILL_ATTRIBUTE_COLD
void stop() noexcept
215 if (!_is_worker_running.exchange(
false))
225 if (_worker_thread.joinable())
227 _worker_thread.join();
230 _worker_thread_id.store(0);
231 _backend_worker_lock.reset(
nullptr);
243 std::lock_guard<std::mutex> lock{_wake_up_mutex};
244 _wake_up_flag =
true;
245 _wake_up_cv.notify_one();
249 std::lock_guard<std::mutex> lock{_wake_up_mutex};
250 _wake_up_flag =
true;
254 _wake_up_cv.notify_one();
260 QUILL_ATTRIBUTE_HOT
void _invoke_poll_hook(std::function<
void()>
const& hook)
const 262 QUILL_TRY { hook(); }
263 #if !defined(QUILL_NO_EXCEPTIONS) 264 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
265 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
270 QUILL_ATTRIBUTE_HOT
void _invoke_poll_end_once(
bool& poll_end_called)
const 277 poll_end_called =
true;
285 QUILL_ATTRIBUTE_COLD
static void _ensure_linker_retains_symbols()
292 std::wstring
const dummy = L
"dummy";
293 QUILL_MAYBE_UNUSED
static auto encode1 = utf8_encode(dummy);
296 QUILL_MAYBE_UNUSED
static auto encode2 =
297 utf8_encode(reinterpret_cast<std::byte const*>(dummy.data()), dummy.size());
305 QUILL_ATTRIBUTE_HOT
void _poll()
308 bool poll_end_called =
false;
315 _update_active_thread_contexts_cache();
318 size_t const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
320 if (cached_transit_events_count != 0)
326 _process_lowest_timestamp_transit_event();
331 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
332 _process_lowest_timestamp_transit_event())
352 _resync_rdtsc_clock();
355 bool const queues_and_events_empty = _check_frontend_queues_and_cached_transit_events_empty();
356 if (queues_and_events_empty)
358 _cleanup_invalidated_thread_contexts();
359 _cleanup_invalidated_loggers();
360 _try_shrink_empty_transit_event_buffers();
365 _invoke_poll_end_once(poll_end_called);
372 std::unique_lock<std::mutex> lock{_wake_up_mutex};
375 _wake_up_cv.wait_for(lock, _options.
sleep_duration, [
this] { return _wake_up_flag; });
378 _wake_up_flag =
false;
381 _resync_rdtsc_clock();
385 std::this_thread::yield();
393 _invoke_poll_end_once(poll_end_called);
424 "transit_events_soft_limit ({}) cannot be greater than transit_events_hard_limit " 425 "({}). Please ensure that the soft limit is less than or equal to the hard limit.",
443 QUILL_ATTRIBUTE_COLD
void _exit()
448 _check_frontend_queues_and_cached_transit_events_empty();
450 if (queues_and_events_empty)
454 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
458 uint64_t
const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
459 if (cached_transit_events_count > 0)
461 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
462 _process_lowest_timestamp_transit_event())
472 _cleanup_invalidated_thread_contexts();
473 _cleanup_invalidated_loggers();
479 QUILL_ATTRIBUTE_HOT
size_t _populate_transit_events_from_frontend_queues()
484 : std::numeric_limits<uint64_t>::max();
486 size_t cached_transit_events_count{0};
488 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
490 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
491 "Invalid queue type in BackendWorker::_read_and_decode_frontend_queues()");
493 if (thread_context->has_unbounded_queue_type())
495 cached_transit_events_count += _read_and_decode_frontend_queue(
496 thread_context->get_spsc_queue_union().unbounded_spsc_queue, thread_context, ts_now);
498 else if (thread_context->has_bounded_queue_type())
500 cached_transit_events_count += _read_and_decode_frontend_queue(
501 thread_context->get_spsc_queue_union().bounded_spsc_queue, thread_context, ts_now);
505 return cached_transit_events_count;
515 template <
typename TFrontendQueue>
516 QUILL_ATTRIBUTE_HOT
size_t _read_and_decode_frontend_queue(TFrontendQueue& frontend_queue,
521 size_t const queue_capacity = frontend_queue.capacity();
522 size_t total_bytes_read{0};
528 if constexpr (std::is_same_v<TFrontendQueue, UnboundedSPSCQueue>)
530 read_pos = _read_unbounded_frontend_queue(frontend_queue, thread_context);
534 read_pos = frontend_queue.prepare_read();
543 std::byte
const*
const read_begin = read_pos;
545 if (!_populate_transit_event_from_frontend_queue(read_pos, thread_context, ts_now))
553 read_pos >= read_begin,
554 "read_pos must be >= read_begin in BackendWorker::_read_and_decode_frontend_queue()");
556 auto const bytes_read =
static_cast<size_t>(read_pos - read_begin);
557 frontend_queue.finish_read(bytes_read);
558 total_bytes_read += bytes_read;
561 }
while ((total_bytes_read < queue_capacity) &&
564 if (total_bytes_read != 0)
569 frontend_queue.commit_read();
572 return thread_context->_transit_event_buffer->size();
576 QUILL_ATTRIBUTE_HOT
bool _populate_transit_event_from_frontend_queue(std::byte*& read_pos,
580 QUILL_ASSERT(thread_context->_transit_event_buffer,
581 "transit_event_buffer is nullptr in " 582 "BackendWorker::_populate_transit_event_from_frontend_queue()");
585 TransitEvent* transit_event = thread_context->_transit_event_buffer->back();
589 "transit_event is nullptr in BackendWorker::_populate_transit_event_from_frontend_queue()");
592 transit_event->formatted_msg,
593 "formatted_msg is nullptr in BackendWorker::_populate_transit_event_from_frontend_queue()");
595 std::memcpy(&transit_event->timestamp, read_pos,
sizeof(transit_event->timestamp));
596 read_pos +=
sizeof(transit_event->timestamp);
598 std::memcpy(&transit_event->macro_metadata, read_pos,
sizeof(transit_event->macro_metadata));
599 read_pos +=
sizeof(transit_event->macro_metadata);
601 std::memcpy(&transit_event->logger_base, read_pos,
sizeof(transit_event->logger_base));
602 read_pos +=
sizeof(transit_event->logger_base);
604 QUILL_ASSERT(transit_event->logger_base,
605 "transit_event->logger_base is nullptr after memcpy from queue");
607 QUILL_ASSERT(transit_event->logger_base->_clock_source == ClockSourceType::Tsc ||
608 transit_event->logger_base->_clock_source == ClockSourceType::System ||
609 transit_event->logger_base->_clock_source == ClockSourceType::User,
610 "transit_event->logger_base->_clock_source has invalid enum value - possible " 611 "memory corruption");
613 if (transit_event->logger_base->_clock_source == ClockSourceType::Tsc)
618 if (QUILL_UNLIKELY(!_rdtsc_clock.load(std::memory_order_relaxed)))
623 _last_rdtsc_resync_time = std::chrono::steady_clock::now();
627 transit_event->timestamp =
628 _rdtsc_clock.load(std::memory_order_relaxed)->time_since_epoch(transit_event->timestamp);
632 if ((transit_event->logger_base->_clock_source != ClockSourceType::User) &&
633 (ts_now != std::numeric_limits<uint64_t>::max()))
638 #if defined(QUILL_ENABLE_ASSERTIONS) || !defined(NDEBUG) 640 auto count_digits = [](uint64_t number)
647 }
while (number != 0);
651 QUILL_ASSERT(count_digits(transit_event->timestamp) == count_digits(ts_now),
652 "Timestamp digits mismatch in " 653 "BackendWorker::_populate_transit_event_from_frontend_queue(), log timestamp " 654 "has different magnitude than current time");
658 if (QUILL_UNLIKELY(transit_event->timestamp > ts_now))
671 std::memcpy(&format_args_decoder, read_pos,
sizeof(format_args_decoder));
672 read_pos +=
sizeof(format_args_decoder);
674 if ((transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy) ||
675 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy) ||
676 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy))
678 _apply_runtime_metadata(read_pos, transit_event);
682 if ((transit_event->macro_metadata->event() != MacroMetadata::Event::Flush) &&
683 (transit_event->macro_metadata->event() != MacroMetadata::Event::LoggerRemovalRequest))
685 format_args_decoder(read_pos, _format_args_store);
687 if (!transit_event->macro_metadata->has_named_args())
689 _populate_formatted_log_message(transit_event, transit_event->macro_metadata->message_format());
694 _named_args_format_template.assign(transit_event->macro_metadata->message_format());
696 if (
auto const search = _named_args_templates.find(_named_args_format_template);
697 search != std::cend(_named_args_templates))
701 auto const& [message_format, arg_names] = search->second;
703 _populate_formatted_log_message(transit_event, message_format.data());
704 _populate_formatted_named_args(transit_event, arg_names);
710 auto const [res_it, inserted] = _named_args_templates.try_emplace(
711 _named_args_format_template,
712 _process_named_args_format_message(transit_event->macro_metadata->message_format()));
714 auto const& [message_format, arg_names] = res_it->second;
719 _populate_formatted_log_message(transit_event, message_format.data());
720 _populate_formatted_named_args(transit_event, arg_names);
724 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::Flush)
728 uintptr_t flush_flag_tmp;
729 std::memcpy(&flush_flag_tmp, read_pos,
sizeof(uintptr_t));
730 transit_event->
flush_flag =
reinterpret_cast<std::atomic<bool>*
>(flush_flag_tmp);
731 read_pos +=
sizeof(uintptr_t);
737 transit_event->macro_metadata->event() == MacroMetadata::Event::LoggerRemovalRequest,
738 "Unexpected event type in BackendWorker::_populate_transit_event_from_frontend_queue(), " 739 "expected LoggerRemovalRequest");
741 uintptr_t logger_removal_flag_tmp;
742 std::memcpy(&logger_removal_flag_tmp, read_pos,
sizeof(uintptr_t));
743 read_pos +=
sizeof(uintptr_t);
746 _logger_removal_flags.emplace(std::string{logger_name},
747 reinterpret_cast<std::atomic<bool>*
>(logger_removal_flag_tmp));
751 thread_context->_transit_event_buffer->push_back();
752 _format_args_store.
clear();
761 QUILL_ATTRIBUTE_HOT
bool has_pending_events_for_caching_when_transit_event_buffer_empty() noexcept
763 _update_active_thread_contexts_cache();
765 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
767 QUILL_ASSERT(thread_context->_transit_event_buffer,
768 "transit_event_buffer is nullptr in " 769 "BackendWorker::has_pending_events_for_caching_when_transit_event_buffer_empty()" 770 ", should be valid in _active_thread_contexts_cache");
772 if (thread_context->_transit_event_buffer->empty())
775 if (thread_context->has_unbounded_queue_type() &&
776 !thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty())
781 if (thread_context->has_bounded_queue_type() &&
782 !thread_context->get_spsc_queue_union().bounded_spsc_queue.empty())
795 QUILL_ATTRIBUTE_HOT
bool _process_lowest_timestamp_transit_event()
798 uint64_t min_ts{std::numeric_limits<uint64_t>::max()};
803 QUILL_ASSERT(tc->_transit_event_buffer,
804 "transit_event_buffer is nullptr in " 805 "BackendWorker::_process_lowest_timestamp_transit_event(), should be valid in " 806 "_active_thread_contexts_cache");
808 TransitEvent const* te = tc->_transit_event_buffer->front();
809 if (te && (min_ts > te->timestamp))
811 min_ts = te->timestamp;
822 TransitEvent* transit_event = thread_context->_transit_event_buffer->front();
825 "transit_event is nullptr in BackendWorker::_process_lowest_timestamp_transit_event() when " 826 "transit_buffer is set");
828 std::atomic<bool>* flush_flag{
nullptr};
830 QUILL_TRY { _process_transit_event(*thread_context, *transit_event, flush_flag); }
831 #if !defined(QUILL_NO_EXCEPTIONS) 832 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
833 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
839 transit_event->
extra_data->named_args.clear();
840 transit_event->
extra_data->runtime_metadata.has_runtime_metadata =
false;
843 thread_context->_transit_event_buffer->pop_front();
857 _cleanup_invalidated_thread_contexts();
860 flush_flag->store(
true);
869 QUILL_ATTRIBUTE_HOT
void _process_transit_event(
ThreadContext const& thread_context,
870 TransitEvent& transit_event, std::atomic<bool>*& flush_flag)
874 if (transit_event.macro_metadata->event() == MacroMetadata::Event::Log)
876 if (transit_event.log_level() != LogLevel::Backtrace)
878 _dispatch_transit_event_to_sinks(transit_event, thread_context.thread_id(),
879 thread_context.thread_name());
886 if (QUILL_UNLIKELY(transit_event.log_level() >=
887 transit_event.logger_base->_backtrace_flush_level.load(std::memory_order_relaxed)))
889 if (transit_event.logger_base->_backtrace_storage)
891 transit_event.logger_base->_backtrace_storage->process(
892 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
893 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
899 if (transit_event.logger_base->_backtrace_storage)
905 transit_event.copy_to(transit_event_copy);
907 transit_event.logger_base->_backtrace_storage->store(
908 std::move(transit_event_copy), thread_context.thread_id(), thread_context.thread_name());
913 QuillError{
"logger->init_backtrace(...) needs to be called first before using " 914 "LOG_BACKTRACE(...)."});
918 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::InitBacktrace)
921 if (!transit_event.logger_base->_backtrace_storage)
924 transit_event.logger_base->_backtrace_storage = std::make_shared<BacktraceStorage>();
927 transit_event.logger_base->_backtrace_storage->set_capacity(static_cast<uint32_t>(std::stoul(
928 std::string{transit_event.formatted_msg->begin(), transit_event.formatted_msg->end()})));
930 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::FlushBacktrace)
932 if (transit_event.logger_base->_backtrace_storage)
935 transit_event.logger_base->_backtrace_storage->process(
936 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
937 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
940 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::Flush)
942 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
957 QUILL_ATTRIBUTE_HOT
void _dispatch_transit_event_to_sinks(
TransitEvent const& transit_event,
958 std::string_view
const& thread_id,
959 std::string_view
const& thread_name)
963 if (QUILL_UNLIKELY(!transit_event.logger_base->_pattern_formatter))
969 if (logger->_pattern_formatter &&
970 (logger->_pattern_formatter->get_options() == transit_event.logger_base->_pattern_formatter_options))
973 transit_event.logger_base->_pattern_formatter = logger->_pattern_formatter;
980 if (!transit_event.logger_base->_pattern_formatter)
983 transit_event.logger_base->_pattern_formatter =
984 std::make_shared<PatternFormatter>(transit_event.logger_base->_pattern_formatter_options);
988 QUILL_ASSERT(transit_event.logger_base->_pattern_formatter,
989 "pattern_formatter is nullptr in BackendWorker::_process_transit_event(), should " 990 "have been created earlier");
993 std::string_view
const log_level_description =
997 std::string_view
const log_level_short_code =
1001 _write_log_statement(
1002 transit_event, thread_id, thread_name, log_level_description, log_level_short_code,
1003 std::string_view{transit_event.formatted_msg->data(), transit_event.formatted_msg->size()});
1009 QUILL_ATTRIBUTE_HOT
void _write_log_statement(
TransitEvent const& transit_event,
1010 std::string_view
const& thread_id,
1011 std::string_view
const& thread_name,
1012 std::string_view
const& log_level_description,
1013 std::string_view
const& log_level_short_code,
1014 std::string_view
const& log_message)
const 1016 std::string_view default_log_statement;
1019 for (
auto& sink : transit_event.logger_base->_sinks)
1021 std::string_view log_to_write;
1024 if (!sink->_override_pattern_formatter_options)
1026 if (default_log_statement.empty())
1030 default_log_statement = transit_event.logger_base->_pattern_formatter->format(
1031 transit_event.timestamp, thread_id, thread_name, _process_id,
1032 transit_event.logger_base->_logger_name, log_level_description, log_level_short_code,
1033 *transit_event.macro_metadata, transit_event.get_named_args(), log_message);
1036 log_to_write = default_log_statement;
1042 if (!sink->_override_pattern_formatter)
1045 sink->_override_pattern_formatter =
1046 std::make_shared<PatternFormatter>(*sink->_override_pattern_formatter_options);
1050 log_to_write = sink->_override_pattern_formatter->format(
1051 transit_event.timestamp, thread_id, thread_name, _process_id,
1052 transit_event.logger_base->_logger_name, log_level_description, log_level_short_code,
1053 *transit_event.macro_metadata, transit_event.get_named_args(), log_message);
1057 if (sink->apply_all_filters(transit_event.macro_metadata, transit_event.timestamp, thread_id,
1058 thread_name, transit_event.logger_base->_logger_name,
1059 transit_event.log_level(), log_message, log_to_write))
1062 sink->write_log(transit_event.macro_metadata, transit_event.timestamp, thread_id,
1063 thread_name, _process_id, transit_event.logger_base->_logger_name,
1064 transit_event.log_level(), log_level_description, log_level_short_code,
1065 transit_event.get_named_args(), log_message, log_to_write);
1074 QUILL_ATTRIBUTE_HOT
void _check_failure_counter(std::function<
void(std::string
const&)>
const& error_notifier) noexcept
1077 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1079 if (thread_context->has_bounded_queue_type())
1081 size_t const failed_messages_cnt = thread_context->get_and_reset_failure_counter();
1083 if (QUILL_UNLIKELY(failed_messages_cnt > 0))
1086 time_t now = time(
nullptr);
1089 strftime(timestamp,
sizeof(timestamp),
"%X", &local_time);
1091 if (thread_context->has_dropping_queue())
1093 error_notifier(fmtquill::format(
"{} Quill INFO: Dropped {} log messages from thread {}",
1094 timestamp, failed_messages_cnt, thread_context->thread_id()));
1096 else if (thread_context->has_blocking_queue())
1099 fmtquill::format(
"{} Quill INFO: Experienced {} blocking occurrences on thread {}",
1100 timestamp, failed_messages_cnt, thread_context->thread_id()));
1112 QUILL_ATTRIBUTE_HOT
static std::pair<std::string, std::vector<std::pair<std::string, std::string>>> _process_named_args_format_message(
1113 std::string_view fmt_template) noexcept
1117 std::string fmt_str;
1118 std::vector<std::pair<std::string, std::string>> keys;
1122 size_t open_bracket_pos = fmt_template.find_first_of(
'{');
1123 while (open_bracket_pos != std::string::npos)
1126 if (
size_t const open_bracket_2_pos = fmt_template.find_first_of(
'{', open_bracket_pos + 1);
1127 open_bracket_2_pos != std::string::npos)
1130 if ((open_bracket_2_pos - 1) == open_bracket_pos)
1132 open_bracket_pos = fmt_template.find_first_of(
'{', open_bracket_2_pos + 1);
1138 size_t close_bracket_pos = fmt_template.find_first_of(
'}', open_bracket_pos + 1);
1139 while (close_bracket_pos != std::string::npos)
1142 if (
size_t const close_bracket_2_pos = fmt_template.find_first_of(
'}', close_bracket_pos + 1);
1143 close_bracket_2_pos != std::string::npos)
1146 if ((close_bracket_2_pos - 1) == close_bracket_pos)
1148 close_bracket_pos = fmt_template.find_first_of(
'}', close_bracket_2_pos + 1);
1154 std::string_view
const text_inside_placeholders =
1155 fmt_template.substr(open_bracket_pos + 1, close_bracket_pos - (open_bracket_pos + 1));
1156 std::string_view arg_syntax;
1157 std::string_view arg_name;
1160 if (
size_t const syntax_separator = text_inside_placeholders.find(
':');
1161 syntax_separator != std::string_view::npos)
1163 arg_syntax = text_inside_placeholders.substr(
1164 syntax_separator, text_inside_placeholders.size() - syntax_separator);
1165 arg_name = text_inside_placeholders.substr(0, syntax_separator);
1169 arg_name = text_inside_placeholders;
1172 fmt_str += fmtquill::format(
1173 "{}{{{}}}", fmt_template.substr(cur_pos, open_bracket_pos - cur_pos), arg_syntax);
1174 cur_pos = close_bracket_pos + 1;
1177 keys.emplace_back(arg_name, arg_syntax);
1182 open_bracket_pos = fmt_template.find_first_of(
'{', close_bracket_pos);
1186 fmt_str += std::string{fmt_template.substr(cur_pos, fmt_template.length() - cur_pos)};
1187 return std::make_pair(fmt_str, keys);
1196 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::byte* _read_unbounded_frontend_queue(
UnboundedSPSCQueue& frontend_queue,
1199 auto const read_result = frontend_queue.
prepare_read();
1201 if (read_result.allocation)
1203 if ((read_result.new_capacity < read_result.previous_capacity) && thread_context->_transit_event_buffer)
1207 thread_context->_transit_event_buffer->request_shrink();
1214 time_t t = time(
nullptr);
1217 strftime(ts, 24,
"%X", std::addressof(p));
1222 fmtquill::format(
"{} Quill INFO: Allocated a new SPSC queue with a capacity of {} KiB " 1223 "(previously {} KiB) from thread {}",
1224 ts, (read_result.new_capacity / 1024),
1225 (read_result.previous_capacity / 1024), thread_context->thread_id()));
1229 return read_result.read_pos;
1232 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT
bool _check_frontend_queues_and_cached_transit_events_empty()
1234 _update_active_thread_contexts_cache();
1236 bool all_empty{
true};
1238 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1240 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
1241 "Invalid queue type in " 1242 "BackendWorker::_check_frontend_queues_and_cached_transit_events_empty()");
1244 if (thread_context->has_unbounded_queue_type())
1246 all_empty &= thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty();
1248 else if (thread_context->has_bounded_queue_type())
1250 all_empty &= thread_context->get_spsc_queue_union().bounded_spsc_queue.empty();
1253 QUILL_ASSERT(thread_context->_transit_event_buffer,
1254 "transit_event_buffer is nullptr in " 1255 "BackendWorker::_check_frontend_queues_and_cached_transit_events_empty(), " 1256 "should be valid in _active_thread_contexts_cache");
1258 all_empty &= thread_context->_transit_event_buffer->empty();
1267 QUILL_ATTRIBUTE_HOT
void _resync_rdtsc_clock()
1269 if (_rdtsc_clock.load(std::memory_order_relaxed))
1272 if (
auto const now = std::chrono::steady_clock::now();
1275 if (_rdtsc_clock.load(std::memory_order_relaxed)->resync(2500))
1277 _last_rdtsc_resync_time = now;
1284 QUILL_ATTRIBUTE_HOT
void _flush_and_run_active_sinks(
bool run_periodic_tasks, std::chrono::milliseconds sink_min_flush_interval)
1292 for (std::shared_ptr<Sink>
const& sink : logger->_sinks)
1294 Sink* logger_sink_ptr = sink.get();
1295 auto search_it = std::find_if(_active_sinks_cache.begin(), _active_sinks_cache.end(),
1296 [logger_sink_ptr](
Sink* elem)
1300 return elem == logger_sink_ptr;
1303 if (search_it == std::end(_active_sinks_cache))
1305 _active_sinks_cache.push_back(logger_sink_ptr);
1314 bool should_flush_sinks{
false};
1315 std::chrono::steady_clock::time_point now;
1317 if (sink_min_flush_interval.count())
1320 now = std::chrono::steady_clock::now();
1321 if ((now - _last_sink_flush_time) > sink_min_flush_interval)
1323 should_flush_sinks =
true;
1329 should_flush_sinks =
true;
1332 for (
auto const& sink : _active_sinks_cache)
1336 if (should_flush_sinks)
1344 #if !defined(QUILL_NO_EXCEPTIONS) 1345 QUILL_CATCH(std::exception
const& e) { _options.
error_notifier(e.what()); }
1346 QUILL_CATCH_ALL() { _options.
error_notifier(std::string{
"Caught unhandled exception."}); }
1349 if (run_periodic_tasks)
1351 sink->run_periodic_tasks();
1356 if (should_flush_sinks && sink_min_flush_interval.count() && !_active_sinks_cache.empty())
1358 _last_sink_flush_time = now;
1361 _active_sinks_cache.clear();
1367 QUILL_ATTRIBUTE_HOT
void _update_active_thread_contexts_cache()
1370 if (QUILL_UNLIKELY(_thread_context_manager.new_thread_context_flag()))
1372 _active_thread_contexts_cache.clear();
1373 _thread_context_manager.for_each_thread_context(
1376 if (!thread_context->_transit_event_buffer)
1379 thread_context->_transit_event_buffer =
1380 std::make_shared<TransitEventBuffer>(_options.transit_event_buffer_initial_capacity);
1385 _active_thread_contexts_cache.push_back(thread_context);
1396 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_thread_contexts()
1398 if (!_thread_context_manager.has_invalid_thread_context())
1403 auto find_invalid_and_empty_thread_context_callback = [](
ThreadContext* thread_context)
1407 if (!thread_context->is_valid())
1409 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
1410 "Invalid queue type in BackendWorker::_update_active_thread_contexts_cache()");
1412 QUILL_ASSERT(thread_context->_transit_event_buffer,
1413 "transit_event_buffer is nullptr in " 1414 "BackendWorker::_update_active_thread_contexts_cache(), should be valid in " 1415 "_active_thread_contexts_cache");
1418 if (thread_context->has_unbounded_queue_type())
1420 return thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty() &&
1421 thread_context->_transit_event_buffer->empty();
1424 if (thread_context->has_bounded_queue_type())
1426 return thread_context->get_spsc_queue_union().bounded_spsc_queue.empty() &&
1427 thread_context->_transit_event_buffer->empty();
1435 auto found_invalid_and_empty_thread_context =
1436 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1437 find_invalid_and_empty_thread_context_callback);
1439 while (QUILL_UNLIKELY(found_invalid_and_empty_thread_context != std::end(_active_thread_contexts_cache)))
1444 _thread_context_manager.remove_shared_invalidated_thread_context(*found_invalid_and_empty_thread_context);
1447 _active_thread_contexts_cache.erase(found_invalid_and_empty_thread_context);
1450 found_invalid_and_empty_thread_context =
1451 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1452 find_invalid_and_empty_thread_context_callback);
1459 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_loggers()
1462 _logger_manager.cleanup_invalidated_loggers(
1467 return _check_frontend_queues_and_cached_transit_events_empty();
1471 if (!_removed_loggers.empty())
1475 _sink_manager.cleanup_unused_sinks();
1477 for (
auto const& removed_logger_name : _removed_loggers)
1480 auto search_it = _logger_removal_flags.find(removed_logger_name);
1481 if (search_it != _logger_removal_flags.end())
1483 search_it->second->store(
true);
1484 _logger_removal_flags.erase(search_it);
1488 _removed_loggers.clear();
1496 QUILL_ATTRIBUTE_HOT
void _try_shrink_empty_transit_event_buffers()
1498 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1500 if (thread_context->_transit_event_buffer)
1502 thread_context->_transit_event_buffer->try_shrink();
1513 static void _format_and_split_arguments(std::vector<std::pair<std::string, std::string>>
const& orig_arg_names,
1514 std::vector<std::pair<std::string, std::string>>& named_args,
1519 std::string format_string;
1520 static constexpr std::string_view delimiter{QUILL_MAGIC_SEPARATOR};
1522 for (
size_t i = 0; i < named_args.size(); ++i)
1526 if ((i < orig_arg_names.size()) && !orig_arg_names[i].second.empty())
1529 format_string += fmtquill::format(
"{{{}}}", orig_arg_names[i].second);
1533 format_string +=
"{}";
1536 if (i < named_args.size() - 1)
1538 format_string += delimiter;
1543 std::string formatted_values_str;
1544 fmtquill::vformat_to(std::back_inserter(formatted_values_str), format_string,
1545 fmtquill::basic_format_args<fmtquill::format_context>{
1546 format_args_store.data(), format_args_store.size()});
1553 while ((end = formatted_values_str.find(delimiter, start)) != std::string::npos)
1555 if (idx < named_args.size())
1557 named_args[idx++].second = formatted_values_str.substr(start, end - start);
1559 start = end + delimiter.length();
1563 if (idx < named_args.size())
1565 named_args[idx].second = formatted_values_str.substr(start);
1575 sanitize_non_printable_chars(
named_arg.second, options);
1580 void _populate_formatted_named_args(
TransitEvent* transit_event,
1581 std::vector<std::pair<std::string, std::string>>
const& arg_names)
1583 transit_event->ensure_extra_data();
1585 auto* named_args = &transit_event->
extra_data->named_args;
1587 named_args->resize(arg_names.size());
1590 for (
size_t i = 0; i < arg_names.size(); ++i)
1592 (*named_args)[i].first = arg_names[i].first;
1595 for (
size_t i = arg_names.size(); i < static_cast<size_t>(_format_args_store.size()); ++i)
1598 named_args->push_back(std::pair<std::string, std::string>(fmtquill::format(
"_{}", i), std::string{}));
1602 QUILL_TRY { _format_and_split_arguments(arg_names, *named_args, _format_args_store, _options); }
1603 #if !defined(QUILL_NO_EXCEPTIONS) 1604 QUILL_CATCH(std::exception
const&)
1613 QUILL_ATTRIBUTE_HOT
void _populate_formatted_log_message(
TransitEvent* transit_event,
char const* message_format)
1615 transit_event->formatted_msg->clear();
1619 fmtquill::vformat_to(std::back_inserter(*transit_event->formatted_msg), message_format,
1620 fmtquill::basic_format_args<fmtquill::format_context>{
1621 _format_args_store.data(), _format_args_store.size()});
1625 sanitize_non_printable_chars(*transit_event->formatted_msg, _options);
1628 #if !defined(QUILL_NO_EXCEPTIONS) 1629 QUILL_CATCH(std::exception
const& e)
1631 transit_event->formatted_msg->clear();
1632 std::string
const error =
1633 fmtquill::format(R
"([Could not format log statement. message: "{}", location: "{}", error: "{}"])", 1634 transit_event->macro_metadata->message_format(), 1635 transit_event->macro_metadata->short_source_location(), e.what()); 1637 transit_event->formatted_msg->append(error); 1643 void _apply_runtime_metadata(std::byte*& read_pos,
TransitEvent* transit_event)
1647 char const*
function;
1650 if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy)
1657 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy)
1664 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy)
1674 QuillError{
"Unexpected event type in _apply_runtime_metadata. This should never happen."});
1682 transit_event->ensure_extra_data();
1683 transit_event->
extra_data->runtime_metadata = temp;
1686 transit_event->macro_metadata = &transit_event->
extra_data->runtime_metadata.macro_metadata;
1689 template <
typename TFormattedMsg>
1690 static void sanitize_non_printable_chars(TFormattedMsg& formatted_msg,
BackendOptions const& options)
1693 bool contains_non_printable_char{
false};
1695 for (
char c : formatted_msg)
1699 contains_non_printable_char =
true;
1704 if (contains_non_printable_char)
1707 std::string
const formatted_msg_copy = {formatted_msg.data(), formatted_msg.size()};
1708 formatted_msg.clear();
1710 for (
char c : formatted_msg_copy)
1714 formatted_msg.append(std::string{c});
1719 constexpr
char hex[] =
"0123456789ABCDEF";
1720 formatted_msg.append(std::string{
'\\'});
1721 formatted_msg.append(std::string{
'x'});
1722 formatted_msg.append(std::string{hex[(c >> 4) & 0xF]});
1723 formatted_msg.append(std::string{hex[c & 0xF]});
1730 friend class quill::ManualBackendWorker;
1732 std::unique_ptr<BackendWorkerLock> _backend_worker_lock;
1734 SinkManager& _sink_manager = SinkManager::instance();
1737 std::thread _worker_thread;
1740 std::vector<std::string> _removed_loggers;
1741 std::vector<ThreadContext*> _active_thread_contexts_cache;
1742 std::vector<Sink*> _active_sinks_cache;
1743 std::unordered_map<std::string, std::pair<std::string, std::vector<std::pair<std::string, std::string>>>> _named_args_templates;
1744 std::unordered_map<std::string, std::atomic<bool>*> _logger_removal_flags;
1745 std::string _named_args_format_template;
1746 std::string _process_id;
1747 std::chrono::steady_clock::time_point _last_rdtsc_resync_time;
1748 std::chrono::steady_clock::time_point _last_sink_flush_time;
1749 std::atomic<uint32_t> _worker_thread_id{0};
1750 std::atomic<bool> _is_worker_running{
false};
1752 alignas(QUILL_CACHE_LINE_ALIGNED) std::atomic<RdtscClock*> _rdtsc_clock{
1754 alignas(QUILL_CACHE_LINE_ALIGNED) std::mutex _wake_up_mutex;
1755 std::condition_variable _wake_up_cv;
1756 bool _wake_up_flag{
false};
1759 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 1760 #pragma warning(pop)
std::unique_ptr< ExtraData > extra_data
buffer for message
Definition: TransitEvent.h:217
A single-producer single-consumer FIFO circular buffer.
Definition: UnboundedSPSCQueue.h:42
void notify()
Wakes up the backend worker thread.
Definition: BackendWorker.h:238
std::function< void()> backend_worker_on_poll_end
Optional hook executed by the backend worker thread at the end of each poll iteration.
Definition: BackendOptions.h:192
Base class for sinks.
Definition: Sink.h:40
QUILL_ATTRIBUTE_COLD void run(BackendOptions const &options)
Starts the backend worker thread.
Definition: BackendWorker.h:138
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:94
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:112
Definition: ThreadContextManager.h:216
void(*)(std::byte *&data, DynamicFormatArgStore &args_store) FormatArgsDecoder
Decode functions.
Definition: Codec.h:399
uint16_t cpu_affinity
Pins the backend to the specified CPU.
Definition: BackendOptions.h:154
std::function< void(std::string const &)> error_notifier
The backend may encounter exceptions that cannot be caught within user threads.
Definition: BackendOptions.h:170
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED std::string get_thread_name()
Returns the name of the thread.
Definition: ThreadUtilities.h:148
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
Definition: LogFunctions.h:261
QUILL_NODISCARD bool is_valid_logger() const noexcept
Checks if the logger is valid.
Definition: LoggerBase.h:110
void for_each_logger(TCallback cb) const
For backend use only.
Definition: LoggerManager.h:135
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
BackendWorker()=default
Constructor.
typename = void for specializations with enable_if
Definition: Codec.h:144
Converts tsc ticks to nanoseconds since epoch.
Definition: RdtscClock.h:36
std::array< std::string, 11 > log_level_descriptions
Holds descriptive names for various log levels used in logging operations.
Definition: BackendOptions.h:247
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:212
This class can be used when you want to run the backend worker on your own thread.
Definition: ManualBackendWorker.h:19
Definition: LoggerBase.h:35
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:129
std::array< std::string, 11 > log_level_short_codes
Short codes or identifiers for each log level.
Definition: BackendOptions.h:257
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:145
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:24
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:190
Definition: SinkManager.h:28
bool check_backend_singleton_instance
Enables a runtime check to detect multiple instances of the backend singleton.
Definition: BackendOptions.h:280
std::atomic< bool > * flush_flag
A unique ptr to save space as these fields not always used.
Definition: TransitEvent.h:218
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
std::function< void()> backend_worker_on_poll_begin
Optional hook executed by the backend worker thread at the start of each poll iteration.
Definition: BackendOptions.h:185
Definition: BackendWorker.h:77
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED uint32_t get_thread_id() noexcept
Returns the os assigned ID of the thread.
Definition: ThreadUtilities.h:198
Definition: LoggerManager.h:33
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:132
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:239
Definition: ThreadContextManager.h:53
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:207
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:224