10 #include "quill/backend/Utf8Conv.h" 13 #include "quill/backend/BackendMdcState.h" 14 #include "quill/backend/BackendOptions.h" 15 #include "quill/backend/BackendUtilities.h" 16 #include "quill/backend/BackendWorkerLock.h" 17 #include "quill/backend/BacktraceStorage.h" 18 #include "quill/backend/PatternFormatter.h" 19 #include "quill/backend/RdtscClock.h" 20 #include "quill/backend/ThreadUtilities.h" 21 #include "quill/backend/TransitEvent.h" 22 #include "quill/backend/TransitEventBuffer.h" 24 #include "quill/core/Attributes.h" 25 #include "quill/core/BoundedSPSCQueue.h" 26 #include "quill/core/ChronoTimeUtils.h" 27 #include "quill/core/Codec.h" 28 #include "quill/core/Common.h" 29 #include "quill/core/DynamicFormatArgStore.h" 30 #include "quill/core/LogLevel.h" 31 #include "quill/core/LoggerBase.h" 32 #include "quill/core/LoggerManager.h" 33 #include "quill/core/MacroMetadata.h" 34 #include "quill/core/MathUtilities.h" 35 #include "quill/core/Metric.h" 36 #include "quill/core/QuillError.h" 37 #include "quill/core/SinkManager.h" 38 #include "quill/core/ThreadContextManager.h" 39 #include "quill/core/ThreadPrimitives.h" 40 #include "quill/core/TimeUtilities.h" 41 #include "quill/core/UnboundedSPSCQueue.h" 42 #include "quill/sinks/Sink.h" 44 #include "quill/bundled/fmt/base.h" 49 #include <condition_variable> 62 #include <string_view> 64 #include <unordered_map> 75 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 77 #pragma warning(disable : 4324) 102 RdtscClock const* rdtsc_clock = _rdtsc_clock.exchange(
nullptr);
107 QUILL_NODISCARD
bool is_running()
const noexcept
109 return _is_worker_running.load(std::memory_order_acquire);
120 QuillError{
"Invalid config, When TSC clock is used backend_thread_sleep_duration should " 121 "not be higher than rdtsc_resync_interval"});
124 RdtscClock const* rdtsc_clock = _rdtsc_clock.load(std::memory_order_acquire);
125 return rdtsc_clock ? rdtsc_clock->time_since_epoch_safe(rdtsc_value) : 0;
134 return _worker_thread_id.load();
143 _ensure_linker_retains_symbols();
145 _has_worker_thread_exited.store(
false);
147 _process_id = std::to_string(get_process_id());
155 _backend_worker_lock = std::make_unique<BackendWorkerLock>(_process_id);
161 QUILL_TRY { _init(options); }
162 #if !defined(QUILL_NO_EXCEPTIONS) 163 QUILL_CATCH(std::exception
const& e)
171 std::string{
"Caught unhandled exception during backend initialization."});
184 #if !defined(QUILL_NO_EXCEPTIONS) 185 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
188 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
197 #if !defined(QUILL_NO_EXCEPTIONS) 198 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
201 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
206 _is_worker_running.store(
true);
209 while (QUILL_LIKELY(_is_worker_running.load(std::memory_order_relaxed)))
212 QUILL_TRY { _poll(); }
213 #if !defined(QUILL_NO_EXCEPTIONS) 214 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
217 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
223 QUILL_TRY { _exit(); }
224 #if !defined(QUILL_NO_EXCEPTIONS) 225 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
228 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
232 _has_worker_thread_exited.store(
true);
236 _worker_thread.swap(worker);
238 while (!_is_worker_running.load(std::memory_order_seq_cst))
248 QUILL_ATTRIBUTE_COLD void stop() noexcept 250 // Stop the backend worker 251 if (!_is_worker_running.exchange(false)) 257 // signal wake up the backend worker thread 260 // Wait the backend thread to join, if backend thread was never started it won't be joinable
261 if (_worker_thread.joinable())
263 _worker_thread.join();
266 if (!_has_worker_thread_exited.load())
271 QUILL_TRY { _exit(); }
272 #if !defined(QUILL_NO_EXCEPTIONS) 273 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
274 QUILL_CATCH_ALL() { _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."}); }
278 _worker_thread_id.store(0);
279 _backend_worker_lock.reset(
nullptr);
287 _named_args_templates = {};
288 _logger_removal_flags = {};
289 _removed_loggers = {};
290 _active_thread_contexts_cache = {};
291 _active_sinks_cache = {};
292 _named_args_format_template = {};
305 std::lock_guard<std::mutex> lock{_wake_up_mutex};
306 _wake_up_flag =
true;
307 _wake_up_cv.notify_one();
311 std::lock_guard<std::mutex> lock{_wake_up_mutex};
312 _wake_up_flag =
true;
316 _wake_up_cv.notify_one();
322 QUILL_ATTRIBUTE_HOT
void _invoke_poll_hook(std::function<
void()>
const& hook)
const 324 QUILL_TRY { hook(); }
325 #if !defined(QUILL_NO_EXCEPTIONS) 326 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
329 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
335 QUILL_ATTRIBUTE_HOT
void _invoke_poll_end_once(
bool& poll_end_called)
const 342 poll_end_called =
true;
346 template <
typename TMessage>
347 static void _notify_error(std::function<
void(std::string
const&)>
const& error_notifier, TMessage&& message)
349 if (static_cast<bool>(error_notifier))
351 QUILL_TRY { error_notifier(static_cast<TMessage&&>(message)); }
352 #if !defined(QUILL_NO_EXCEPTIONS) 366 QUILL_ATTRIBUTE_COLD
static void _ensure_linker_retains_symbols()
373 std::wstring
const dummy = L
"dummy";
374 QUILL_MAYBE_UNUSED
static auto encode1 = utf8_encode(dummy);
377 QUILL_MAYBE_UNUSED
static auto encode2 =
378 utf8_encode(reinterpret_cast<std::byte const*>(dummy.data()), dummy.size());
386 QUILL_ATTRIBUTE_HOT
void _poll()
389 bool poll_end_called =
false;
396 _update_active_thread_contexts_cache();
399 size_t const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
401 if (cached_transit_events_count != 0)
407 _process_lowest_timestamp_transit_event();
412 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
413 _process_lowest_timestamp_transit_event())
433 _resync_rdtsc_clock();
436 bool const queues_and_events_empty = _check_frontend_queues_and_cached_transit_events_empty();
437 if (queues_and_events_empty)
439 _cleanup_invalidated_thread_contexts();
440 _cleanup_invalidated_loggers();
441 _try_shrink_empty_transit_event_buffers();
446 _invoke_poll_end_once(poll_end_called);
453 std::unique_lock<std::mutex> lock{_wake_up_mutex};
456 _wake_up_cv.wait_for(lock, _options.
sleep_duration, [
this] { return _wake_up_flag; });
459 _wake_up_flag =
false;
462 _resync_rdtsc_clock();
474 _invoke_poll_end_once(poll_end_called);
505 "transit_events_soft_limit ({}) cannot be greater than transit_events_hard_limit " 506 "({}). Please ensure that the soft limit is less than or equal to the hard limit.",
520 _last_output_timestamp = 0;
525 _update_active_thread_contexts_cache(
true);
530 _worker_thread_id.store(worker_thread_id);
531 LoggerBase::set_current_thread_is_backend_thread(
true);
535 void _clear_backend_thread_flag() noexcept
537 LoggerBase::set_current_thread_is_backend_thread(
false);
543 QUILL_ATTRIBUTE_COLD
void _exit()
548 _check_frontend_queues_and_cached_transit_events_empty();
550 if (queues_and_events_empty)
554 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
558 uint64_t
const cached_transit_events_count = _populate_transit_events_from_frontend_queues();
559 if (cached_transit_events_count > 0)
561 while (!has_pending_events_for_caching_when_transit_event_buffer_empty() &&
562 _process_lowest_timestamp_transit_event())
572 _cleanup_invalidated_thread_contexts();
573 _cleanup_invalidated_loggers();
580 RdtscClock const* rdtsc_clock = _rdtsc_clock.exchange(
nullptr, std::memory_order_acq_rel);
583 _clear_backend_thread_flag();
589 QUILL_ATTRIBUTE_HOT
size_t _populate_transit_events_from_frontend_queues()
595 : std::numeric_limits<uint64_t>::max();
597 size_t total_cached_transit_events_count{0};
599 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
601 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
602 "Invalid queue type in BackendWorker::_read_and_decode_frontend_queues()");
604 if (thread_context->has_unbounded_queue_type())
606 total_cached_transit_events_count += _read_and_decode_frontend_queue(
607 thread_context->get_spsc_queue_union().unbounded_spsc_queue, thread_context, ts_now);
609 else if (thread_context->has_bounded_queue_type())
611 total_cached_transit_events_count += _read_and_decode_frontend_queue(
612 thread_context->get_spsc_queue_union().bounded_spsc_queue, thread_context, ts_now);
616 return total_cached_transit_events_count;
626 template <
typename TFrontendQueue>
627 QUILL_ATTRIBUTE_HOT
size_t _read_and_decode_frontend_queue(TFrontendQueue& frontend_queue,
632 size_t const queue_capacity = frontend_queue.capacity();
633 size_t total_bytes_read{0};
639 if constexpr (std::is_same_v<TFrontendQueue, UnboundedSPSCQueue>)
641 read_pos = _read_unbounded_frontend_queue(frontend_queue, thread_context);
645 read_pos = frontend_queue.prepare_read();
654 std::byte
const*
const read_begin = read_pos;
656 if (!_populate_transit_event_from_frontend_queue(read_pos, thread_context, ts_now))
664 read_pos >= read_begin,
665 "read_pos must be >= read_begin in BackendWorker::_read_and_decode_frontend_queue()");
667 auto const bytes_read =
static_cast<size_t>(read_pos - read_begin);
668 frontend_queue.finish_read(bytes_read);
669 total_bytes_read += bytes_read;
672 }
while ((total_bytes_read < queue_capacity) &&
675 if (total_bytes_read != 0)
680 frontend_queue.commit_read();
683 return thread_context->_transit_event_buffer->size();
687 QUILL_ATTRIBUTE_HOT
bool _populate_transit_event_from_frontend_queue(std::byte*& read_pos,
691 QUILL_ASSERT(thread_context->_transit_event_buffer,
692 "transit_event_buffer is nullptr in " 693 "BackendWorker::_populate_transit_event_from_frontend_queue()");
698 QUILL_TRY { transit_event = thread_context->_transit_event_buffer->back(); }
699 #if !defined(QUILL_NO_EXCEPTIONS) 709 "Quill ERROR: Failed to expand the transit event buffer (allocation failure). " 710 "The log message remains in the thread queue and will be retried");
717 "transit_event is nullptr in BackendWorker::_populate_transit_event_from_frontend_queue()");
720 transit_event->formatted_msg,
721 "formatted_msg is nullptr in BackendWorker::_populate_transit_event_from_frontend_queue()");
724 "FormatArgsDecoder must fit in uintptr_t for packed header decoding");
725 static_assert(
sizeof(uintptr_t) <=
sizeof(uint64_t),
726 "Packed header decoding requires pointers to fit in 64 bits");
728 uint64_t header_words[4];
729 std::memcpy(header_words, read_pos,
sizeof(header_words));
730 read_pos +=
sizeof(header_words);
732 transit_event->timestamp = header_words[0];
733 transit_event->macro_metadata =
734 reinterpret_cast<MacroMetadata const*
>(
static_cast<uintptr_t
>(header_words[1]));
735 transit_event->logger_base =
reinterpret_cast<LoggerBase*
>(
static_cast<uintptr_t
>(header_words[2]));
737 QUILL_ASSERT(transit_event->logger_base,
738 "transit_event->logger_base is nullptr after memcpy from queue");
740 QUILL_ASSERT(transit_event->logger_base->_clock_source == ClockSourceType::Tsc ||
741 transit_event->logger_base->_clock_source == ClockSourceType::System ||
742 transit_event->logger_base->_clock_source == ClockSourceType::User,
743 "transit_event->logger_base->_clock_source has invalid enum value - possible " 744 "memory corruption");
746 if (transit_event->logger_base->_clock_source == ClockSourceType::Tsc)
751 if (QUILL_UNLIKELY(!_rdtsc_clock.load(std::memory_order_relaxed)))
756 _last_rdtsc_resync_time = std::chrono::steady_clock::now();
760 transit_event->timestamp =
761 _rdtsc_clock.load(std::memory_order_relaxed)->time_since_epoch(transit_event->timestamp);
765 if ((transit_event->logger_base->_clock_source != ClockSourceType::User) &&
766 (ts_now != std::numeric_limits<uint64_t>::max()))
771 #if defined(QUILL_ENABLE_ASSERTIONS) || !defined(NDEBUG) 773 auto count_digits = [](uint64_t number)
780 }
while (number != 0);
784 QUILL_ASSERT(count_digits(transit_event->timestamp) == count_digits(ts_now),
785 "Timestamp digits mismatch in " 786 "BackendWorker::_populate_transit_event_from_frontend_queue(), log timestamp " 787 "has different magnitude than current time");
791 if (QUILL_UNLIKELY(transit_event->timestamp > ts_now))
801 bool const is_mdc_event = (transit_event->macro_metadata->event() == MacroMetadata::Event::MdcSet) ||
802 (transit_event->macro_metadata->event() == MacroMetadata::Event::MdcErase) ||
803 (transit_event->macro_metadata->event() == MacroMetadata::Event::MdcClear);
805 if (transit_event->macro_metadata->event() != MacroMetadata::Event::Metric)
807 uintptr_t
const decoder_bits =
static_cast<uintptr_t
>(header_words[3]);
809 std::memcpy(&format_args_decoder, &decoder_bits,
sizeof(format_args_decoder));
813 _apply_mdc_event(*thread_context, transit_event->macro_metadata->event(), format_args_decoder, read_pos);
817 if ((transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy) ||
818 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy) ||
819 (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy))
821 _apply_runtime_metadata(read_pos, transit_event);
825 if ((transit_event->macro_metadata->event() != MacroMetadata::Event::Flush) &&
826 (transit_event->macro_metadata->event() != MacroMetadata::Event::LoggerRemovalRequest))
828 format_args_decoder(read_pos, _format_args_store);
830 if (!transit_event->macro_metadata->has_named_args())
832 _populate_formatted_log_message(transit_event, transit_event->macro_metadata->message_format());
837 _named_args_format_template.assign(transit_event->macro_metadata->message_format());
839 if (
auto const search = _named_args_templates.find(_named_args_format_template);
840 search != std::cend(_named_args_templates))
844 auto const& [message_format, arg_names] = search->second;
846 _populate_formatted_log_message(transit_event, message_format.data());
847 _populate_formatted_named_args(transit_event, arg_names);
853 auto const [res_it, inserted] = _named_args_templates.try_emplace(
854 _named_args_format_template,
855 _process_named_args_format_message(transit_event->macro_metadata->message_format()));
857 auto const& [message_format, arg_names] = res_it->second;
862 _populate_formatted_log_message(transit_event, message_format.data());
863 _populate_formatted_named_args(transit_event, arg_names);
867 _set_transit_event_mdc(*thread_context, transit_event);
869 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::Flush)
873 uintptr_t flush_flag_tmp;
874 std::memcpy(&flush_flag_tmp, read_pos,
sizeof(uintptr_t));
875 transit_event->set_flush_flag(
reinterpret_cast<std::atomic<bool>*
>(flush_flag_tmp));
876 read_pos +=
sizeof(uintptr_t);
878 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LoggerRemovalRequest)
881 uintptr_t logger_removal_flag_tmp;
882 std::memcpy(&logger_removal_flag_tmp, read_pos,
sizeof(uintptr_t));
883 read_pos +=
sizeof(uintptr_t);
886 _logger_removal_flags.emplace(
887 std::string{logger_name},
reinterpret_cast<std::atomic<bool>*
>(logger_removal_flag_tmp));
893 "Unhandled event type in BackendWorker::_populate_transit_event_from_frontend_queue()");
900 std::memcpy(&metric_value, &header_words[3],
sizeof(metric_value));
901 transit_event->set_metric_value(metric_value);
907 thread_context->_transit_event_buffer->push_back();
910 _format_args_store.
clear();
919 QUILL_ATTRIBUTE_HOT
bool has_pending_events_for_caching_when_transit_event_buffer_empty() noexcept
921 _update_active_thread_contexts_cache();
923 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
925 QUILL_ASSERT(thread_context->_transit_event_buffer,
926 "transit_event_buffer is nullptr in " 927 "BackendWorker::has_pending_events_for_caching_when_transit_event_buffer_empty()" 928 ", should be valid in _active_thread_contexts_cache");
930 if (thread_context->_transit_event_buffer->empty())
933 if (thread_context->has_unbounded_queue_type() &&
934 !thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty())
939 if (thread_context->has_bounded_queue_type() &&
940 !thread_context->get_spsc_queue_union().bounded_spsc_queue.empty())
953 QUILL_ATTRIBUTE_HOT
bool _process_lowest_timestamp_transit_event()
956 uint64_t min_ts{std::numeric_limits<uint64_t>::max()};
961 QUILL_ASSERT(tc->_transit_event_buffer,
962 "transit_event_buffer is nullptr in " 963 "BackendWorker::_process_lowest_timestamp_transit_event(), should be valid in " 964 "_active_thread_contexts_cache");
966 TransitEvent const* te = tc->_transit_event_buffer->front();
967 if (te && (min_ts > te->timestamp))
969 min_ts = te->timestamp;
980 TransitEvent* transit_event = thread_context->_transit_event_buffer->front();
983 "transit_event is nullptr in BackendWorker::_process_lowest_timestamp_transit_event() when " 984 "transit_buffer is set");
986 std::atomic<bool>* flush_flag{
nullptr};
988 QUILL_TRY { _process_transit_event(*thread_context, *transit_event, flush_flag); }
989 #if !defined(QUILL_NO_EXCEPTIONS) 990 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
993 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1000 transit_event->
extra_data->named_args.clear();
1002 transit_event->
extra_data->runtime_metadata.has_runtime_metadata =
false;
1009 thread_context->_transit_event_buffer->pop_front();
1023 _cleanup_invalidated_thread_contexts();
1026 flush_flag->store(
true);
1035 QUILL_ATTRIBUTE_HOT
void _process_transit_event(
ThreadContext const& thread_context,
1036 TransitEvent& transit_event, std::atomic<bool>*& flush_flag)
1040 if (transit_event.macro_metadata->event() == MacroMetadata::Event::Log)
1042 if (transit_event.log_level() != LogLevel::Backtrace)
1044 _ensure_monotonic_output_timestamp(transit_event);
1045 _dispatch_transit_event_to_sinks(transit_event, thread_context.thread_id(),
1046 thread_context.thread_name());
1053 if (QUILL_UNLIKELY(transit_event.log_level() >=
1054 transit_event.logger_base->_backtrace_flush_level.load(std::memory_order_relaxed)))
1056 if (transit_event.logger_base->_backtrace_storage)
1058 transit_event.logger_base->_backtrace_storage->process(
1059 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
1060 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
1066 if (transit_event.logger_base->_backtrace_storage)
1072 transit_event.copy_to(transit_event_copy);
1074 transit_event.logger_base->_backtrace_storage->store(
1075 std::move(transit_event_copy), thread_context.thread_id(), thread_context.thread_name());
1080 QuillError{
"logger->init_backtrace(...) needs to be called first before using " 1081 "LOG_BACKTRACE(...)."});
1085 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::InitBacktrace)
1088 if (!transit_event.logger_base->_backtrace_storage)
1091 transit_event.logger_base->_backtrace_storage = std::make_shared<BacktraceStorage>();
1094 transit_event.logger_base->_backtrace_storage->set_capacity(static_cast<uint32_t>(std::stoul(
1095 std::string{transit_event.formatted_msg->begin(), transit_event.formatted_msg->end()})));
1097 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::FlushBacktrace)
1099 if (transit_event.logger_base->_backtrace_storage)
1102 transit_event.logger_base->_backtrace_storage->process(
1103 [
this](
TransitEvent const& te, std::string_view thread_id, std::string_view thread_name)
1104 { _dispatch_transit_event_to_sinks(te, thread_id, thread_name); });
1107 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::Flush)
1109 _flush_and_run_active_sinks(
false, std::chrono::milliseconds{0});
1112 flush_flag = transit_event.flush_flag();
1115 transit_event.reset_payload();
1119 else if (transit_event.macro_metadata->event() == MacroMetadata::Event::Metric)
1121 _ensure_monotonic_output_timestamp(transit_event);
1122 _write_metric_sample(transit_event, thread_context.thread_id(), thread_context.thread_name());
1126 transit_event.reset_payload();
1130 QUILL_ASSERT(transit_event.macro_metadata->event() == MacroMetadata::Event::LoggerRemovalRequest,
1131 "Unexpected transit event type in BackendWorker::_process_transit_event()");
1142 QUILL_ATTRIBUTE_HOT
void _ensure_monotonic_output_timestamp(
TransitEvent& transit_event) noexcept
1149 if (QUILL_UNLIKELY(transit_event.timestamp < _last_output_timestamp))
1151 transit_event.timestamp = (_last_output_timestamp == std::numeric_limits<uint64_t>::max())
1152 ? _last_output_timestamp
1153 : _last_output_timestamp + 1;
1156 _last_output_timestamp = transit_event.timestamp;
1162 QUILL_ATTRIBUTE_HOT
void _dispatch_transit_event_to_sinks(
TransitEvent const& transit_event,
1163 std::string_view thread_id, std::string_view thread_name)
1167 if (QUILL_UNLIKELY(!transit_event.logger_base->_pattern_formatter))
1173 if (logger->_pattern_formatter &&
1174 (logger->_pattern_formatter->get_options() == transit_event.logger_base->_pattern_formatter_options))
1177 transit_event.logger_base->_pattern_formatter = logger->_pattern_formatter;
1184 if (!transit_event.logger_base->_pattern_formatter)
1187 transit_event.logger_base->_pattern_formatter =
1188 std::make_shared<PatternFormatter>(transit_event.logger_base->_pattern_formatter_options);
1192 QUILL_ASSERT(transit_event.logger_base->_pattern_formatter,
1193 "pattern_formatter is nullptr in BackendWorker::_process_transit_event(), should " 1194 "have been created earlier");
1197 std::string_view
const log_level_description =
1201 std::string_view
const log_level_short_code =
1205 _write_log_statement(
1206 transit_event, thread_id, thread_name, log_level_description, log_level_short_code,
1207 std::string_view{transit_event.formatted_msg->data(), transit_event.formatted_msg->size()});
1213 QUILL_ATTRIBUTE_HOT
void _write_metric_sample(
TransitEvent const& transit_event, std::string_view thread_id,
1214 std::string_view thread_name)
const 1217 transit_event.macro_metadata,
1218 "transit_event.macro_metadata is nullptr in BackendWorker::_write_metric_sample()");
1219 QUILL_ASSERT(transit_event.macro_metadata->event() == MacroMetadata::Event::Metric,
1220 "Unexpected transit event type in BackendWorker::_write_metric_sample()");
1224 auto const* metric_metadata =
static_cast<MetricMetadata const*
>(transit_event.macro_metadata);
1225 double const metric_value = transit_event.metric_value();
1227 for (
auto& sink : transit_event.logger_base->_sinks)
1229 sink->write_metric(metric_metadata, transit_event.timestamp, thread_id, thread_name,
1230 _process_id, transit_event.logger_base->_logger_name, metric_value);
1237 QUILL_ATTRIBUTE_HOT
void _write_log_statement(
TransitEvent const& transit_event,
1238 std::string_view
const& thread_id,
1239 std::string_view
const& thread_name,
1240 std::string_view
const& log_level_description,
1241 std::string_view
const& log_level_short_code,
1242 std::string_view
const& log_message)
const 1244 std::string_view default_log_statement;
1247 for (
auto& sink : transit_event.logger_base->_sinks)
1249 std::string_view log_to_write;
1252 if (!sink->_override_pattern_formatter_options)
1254 if (default_log_statement.empty())
1258 default_log_statement = transit_event.logger_base->_pattern_formatter->format(
1259 transit_event.timestamp, thread_id, thread_name, _process_id, transit_event.logger_base->_logger_name,
1260 log_level_description, log_level_short_code, *transit_event.macro_metadata,
1261 transit_event.get_named_args(), log_message, transit_event.mdc());
1264 log_to_write = default_log_statement;
1270 if (!sink->_override_pattern_formatter)
1273 sink->_override_pattern_formatter =
1274 std::make_shared<PatternFormatter>(*sink->_override_pattern_formatter_options);
1278 log_to_write = sink->_override_pattern_formatter->format(
1279 transit_event.timestamp, thread_id, thread_name, _process_id, transit_event.logger_base->_logger_name,
1280 log_level_description, log_level_short_code, *transit_event.macro_metadata,
1281 transit_event.get_named_args(), log_message, transit_event.mdc());
1285 if (sink->apply_all_filters(transit_event.macro_metadata, transit_event.timestamp, thread_id,
1286 thread_name, transit_event.logger_base->_logger_name,
1287 transit_event.log_level(), log_message, log_to_write))
1290 sink->write_log(transit_event.macro_metadata, transit_event.timestamp, thread_id,
1291 thread_name, _process_id, transit_event.logger_base->_logger_name,
1292 transit_event.log_level(), log_level_description, log_level_short_code,
1293 transit_event.get_named_args(), log_message, log_to_write);
1302 QUILL_ATTRIBUTE_HOT
void _check_failure_counter(std::function<
void(std::string
const&)>
const& error_notifier)
1304 if (!error_notifier)
1309 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1311 size_t const failed_events_cnt = thread_context->get_and_reset_failure_counter();
1313 if (QUILL_UNLIKELY(failed_events_cnt > 0))
1316 time_t now = time(
nullptr);
1319 strftime(timestamp,
sizeof(timestamp),
"%X", &local_time);
1321 if (thread_context->has_dropping_queue())
1323 if (thread_context->has_unbounded_queue_type())
1325 _notify_error(error_notifier,
1326 fmtquill::format(
"{} Quill INFO: Reached the maximum configured " 1327 "unbounded queue capacity and dropped " 1328 "{} events from thread {}",
1329 timestamp, failed_events_cnt, thread_context->thread_id()));
1333 _notify_error(error_notifier,
1334 fmtquill::format(
"{} Quill INFO: Dropped {} events from thread {}",
1335 timestamp, failed_events_cnt, thread_context->thread_id()));
1338 else if (thread_context->has_blocking_queue())
1340 if (thread_context->has_unbounded_queue_type())
1345 "{} Quill INFO: Reached the maximum configured unbounded queue capacity and " 1346 "experienced {} blocking occurrences on thread {}",
1347 timestamp, failed_events_cnt, thread_context->thread_id()));
1353 fmtquill::format(
"{} Quill INFO: Experienced {} blocking occurrences on thread {}",
1354 timestamp, failed_events_cnt, thread_context->thread_id()));
1366 QUILL_ATTRIBUTE_HOT
static std::pair<std::string, std::vector<std::pair<std::string, std::string>>> _process_named_args_format_message(
1367 std::string_view fmt_template)
1371 std::string fmt_str;
1372 std::vector<std::pair<std::string, std::string>> keys;
1373 fmt_str.reserve(fmt_template.size());
1376 while (pos < fmt_template.size())
1378 if (fmt_template[pos] !=
'{')
1380 fmt_str += fmt_template[pos];
1385 if ((pos + 1 < fmt_template.size()) && (fmt_template[pos + 1] ==
'{'))
1392 size_t const placeholder_start = pos;
1395 size_t const close_bracket_pos = _find_placeholder_closing_brace(fmt_template, pos);
1396 if (close_bracket_pos == std::string_view::npos)
1398 fmt_str += std::string{fmt_template.substr(placeholder_start)};
1402 std::string_view
const text_inside_placeholders = fmt_template.substr(pos, close_bracket_pos - pos);
1403 std::string_view arg_syntax;
1404 std::string_view arg_name;
1406 if (
size_t const syntax_separator = text_inside_placeholders.find(
':');
1407 syntax_separator != std::string_view::npos)
1409 arg_syntax = text_inside_placeholders.substr(
1410 syntax_separator, text_inside_placeholders.size() - syntax_separator);
1411 arg_name = text_inside_placeholders.substr(0, syntax_separator);
1415 arg_name = text_inside_placeholders;
1418 std::string
const normalized_arg_syntax = _normalize_named_arg_syntax(arg_syntax);
1421 fmt_str += normalized_arg_syntax;
1424 keys.emplace_back(_make_unique_named_arg_key(keys, arg_name), normalized_arg_syntax);
1425 pos = close_bracket_pos + 1;
1428 return std::make_pair(fmt_str, keys);
1431 QUILL_NODISCARD
static std::string _make_unique_named_arg_key(
1432 std::vector<std::pair<std::string, std::string>>
const& existing_keys, std::string_view arg_name)
1434 std::string key{arg_name};
1436 for (
size_t suffix = 1;; ++suffix)
1438 bool key_already_exists =
false;
1439 for (
auto const& existing_key : existing_keys)
1441 if (existing_key.first == key)
1443 key_already_exists =
true;
1448 if (!key_already_exists)
1453 key = std::string{arg_name};
1455 key += std::to_string(suffix);
1459 QUILL_NODISCARD
static size_t _find_placeholder_closing_brace(std::string_view text,
size_t pos) noexcept
1461 size_t nested_replacement_fields = 0;
1463 while (pos < text.size())
1465 if (text[pos] ==
'{')
1467 if ((pos + 1 < text.size()) && (text[pos + 1] ==
'{'))
1473 ++nested_replacement_fields;
1478 if (text[pos] ==
'}')
1480 if (nested_replacement_fields == 0)
1485 --nested_replacement_fields;
1491 return std::string_view::npos;
1494 QUILL_NODISCARD
static std::string _normalize_named_arg_syntax(std::string_view arg_syntax)
1496 std::string normalized;
1497 normalized.reserve(arg_syntax.size());
1500 while (pos < arg_syntax.size())
1502 if (arg_syntax[pos] !=
'{')
1504 normalized += arg_syntax[pos];
1509 if ((pos + 1 < arg_syntax.size()) && (arg_syntax[pos + 1] ==
'{'))
1516 size_t const close_bracket_pos = _find_placeholder_closing_brace(arg_syntax, pos + 1);
1517 if (close_bracket_pos == std::string_view::npos)
1519 normalized += std::string{arg_syntax.substr(pos)};
1524 pos = close_bracket_pos + 1;
1536 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::byte* _read_unbounded_frontend_queue(
UnboundedSPSCQueue& frontend_queue,
1539 auto const read_result = frontend_queue.
prepare_read();
1541 if (read_result.allocation)
1543 if ((read_result.new_capacity < read_result.previous_capacity) && thread_context->_transit_event_buffer)
1547 thread_context->_transit_event_buffer->request_shrink();
1554 time_t t = time(
nullptr);
1557 strftime(ts, 24,
"%X", std::addressof(p));
1563 fmtquill::format(
"{} Quill INFO: Allocated a new SPSC queue with a capacity of {} KiB " 1564 "(previously {} KiB) from thread {}",
1565 ts, (read_result.new_capacity / 1024),
1566 (read_result.previous_capacity / 1024), thread_context->thread_id()));
1570 return read_result.read_pos;
1573 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT
bool _check_frontend_queues_and_cached_transit_events_empty()
1575 _update_active_thread_contexts_cache();
1577 bool all_empty{
true};
1579 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1581 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
1582 "Invalid queue type in " 1583 "BackendWorker::_check_frontend_queues_and_cached_transit_events_empty()");
1585 if (thread_context->has_unbounded_queue_type())
1587 all_empty &= thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty();
1589 else if (thread_context->has_bounded_queue_type())
1591 all_empty &= thread_context->get_spsc_queue_union().bounded_spsc_queue.empty();
1594 QUILL_ASSERT(thread_context->_transit_event_buffer,
1595 "transit_event_buffer is nullptr in " 1596 "BackendWorker::_check_frontend_queues_and_cached_transit_events_empty(), " 1597 "should be valid in _active_thread_contexts_cache");
1599 all_empty &= thread_context->_transit_event_buffer->empty();
1608 QUILL_ATTRIBUTE_HOT
void _resync_rdtsc_clock()
1610 if (_rdtsc_clock.load(std::memory_order_relaxed))
1613 if (
auto const now = std::chrono::steady_clock::now();
1616 if (_rdtsc_clock.load(std::memory_order_relaxed)->resync(2500))
1618 _last_rdtsc_resync_time = now;
1625 QUILL_ATTRIBUTE_HOT
void _flush_and_run_active_sinks(
bool run_periodic_tasks, std::chrono::milliseconds sink_min_flush_interval)
1633 for (std::shared_ptr<Sink>
const& sink : logger->_sinks)
1635 Sink* logger_sink_ptr = sink.get();
1636 auto search_it = std::find_if(_active_sinks_cache.begin(), _active_sinks_cache.end(),
1637 [logger_sink_ptr](
Sink* elem)
1641 return elem == logger_sink_ptr;
1644 if (search_it == std::end(_active_sinks_cache))
1646 _active_sinks_cache.push_back(logger_sink_ptr);
1655 bool should_flush_sinks{
false};
1656 std::chrono::steady_clock::time_point now;
1658 if (sink_min_flush_interval.count())
1661 now = std::chrono::steady_clock::now();
1662 if ((now - _last_sink_flush_time) > sink_min_flush_interval)
1664 should_flush_sinks =
true;
1670 should_flush_sinks =
true;
1673 for (
auto const& sink : _active_sinks_cache)
1677 if (should_flush_sinks)
1685 #if !defined(QUILL_NO_EXCEPTIONS) 1686 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
1689 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1693 if (run_periodic_tasks)
1695 QUILL_TRY { sink->run_periodic_tasks(); }
1696 #if !defined(QUILL_NO_EXCEPTIONS) 1697 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
1700 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1707 if (should_flush_sinks && sink_min_flush_interval.count() && !_active_sinks_cache.empty())
1709 _last_sink_flush_time = now;
1712 _active_sinks_cache.clear();
1718 QUILL_ATTRIBUTE_HOT
void _update_active_thread_contexts_cache(
bool force =
false)
1721 if (QUILL_UNLIKELY(force || _thread_context_manager.new_thread_context_flag()))
1723 _active_thread_contexts_cache.clear();
1724 _thread_context_manager.for_each_thread_context(
1727 if (!thread_context->_transit_event_buffer)
1730 thread_context->_transit_event_buffer =
1731 std::make_shared<TransitEventBuffer>(_options.transit_event_buffer_initial_capacity);
1736 _active_thread_contexts_cache.push_back(thread_context);
1747 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_thread_contexts()
1749 if (!_thread_context_manager.has_invalid_thread_context())
1754 auto find_invalid_and_empty_thread_context_callback = [](
ThreadContext* thread_context)
1758 if (!thread_context->is_valid())
1760 QUILL_ASSERT(thread_context->has_unbounded_queue_type() || thread_context->has_bounded_queue_type(),
1761 "Invalid queue type in BackendWorker::_update_active_thread_contexts_cache()");
1763 QUILL_ASSERT(thread_context->_transit_event_buffer,
1764 "transit_event_buffer is nullptr in " 1765 "BackendWorker::_update_active_thread_contexts_cache(), should be valid in " 1766 "_active_thread_contexts_cache");
1769 if (thread_context->has_unbounded_queue_type())
1771 return thread_context->get_spsc_queue_union().unbounded_spsc_queue.empty() &&
1772 thread_context->_transit_event_buffer->empty();
1775 if (thread_context->has_bounded_queue_type())
1777 return thread_context->get_spsc_queue_union().bounded_spsc_queue.empty() &&
1778 thread_context->_transit_event_buffer->empty();
1786 auto found_invalid_and_empty_thread_context =
1787 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1788 find_invalid_and_empty_thread_context_callback);
1790 while (QUILL_UNLIKELY(found_invalid_and_empty_thread_context != std::end(_active_thread_contexts_cache)))
1795 _thread_context_manager.remove_shared_invalidated_thread_context(*found_invalid_and_empty_thread_context);
1798 _active_thread_contexts_cache.erase(found_invalid_and_empty_thread_context);
1801 found_invalid_and_empty_thread_context =
1802 std::find_if(_active_thread_contexts_cache.begin(), _active_thread_contexts_cache.end(),
1803 find_invalid_and_empty_thread_context_callback);
1810 QUILL_ATTRIBUTE_HOT
void _cleanup_invalidated_loggers()
1813 _logger_manager.cleanup_invalidated_loggers(
1818 return _check_frontend_queues_and_cached_transit_events_empty();
1822 if (!_removed_loggers.empty())
1826 _sink_manager.cleanup_unused_sinks();
1828 for (
auto const& removed_logger_name : _removed_loggers)
1831 auto search_it = _logger_removal_flags.find(removed_logger_name);
1832 if (search_it != _logger_removal_flags.end())
1834 search_it->second->store(
true);
1835 _logger_removal_flags.erase(search_it);
1839 _removed_loggers.clear();
1847 QUILL_ATTRIBUTE_HOT
void _try_shrink_empty_transit_event_buffers()
1849 for (
ThreadContext* thread_context : _active_thread_contexts_cache)
1851 if (thread_context->_transit_event_buffer)
1853 thread_context->_transit_event_buffer->try_shrink();
1858 void _apply_mdc_event(
ThreadContext& thread_context, MacroMetadata::Event event,
1861 if (event == MacroMetadata::Event::MdcSet)
1863 format_args_decoder(read_pos, _format_args_store);
1865 QUILL_ASSERT((_format_args_store.size() % 2) == 0,
1866 "MdcSet decoded argument count mismatch in BackendWorker::_apply_mdc_event()");
1868 if (!thread_context._backend_mdc_state)
1870 thread_context._backend_mdc_state = std::make_shared<BackendMdcState>(_options.
mdc_format_pattern);
1874 auto const field_count =
static_cast<uint32_t
>(_format_args_store.size() / 2);
1876 _mdc_fields.clear();
1877 _mdc_fields.reserve(field_count);
1881 for (uint32_t i = 0; i < field_count; ++i)
1883 int const key_index =
static_cast<int>(i * 2u);
1884 int const value_index = key_index + 1;
1886 std::string
const key = fmtquill::vformat(
1887 "{}", fmtquill::basic_format_args<fmtquill::format_context>{_format_args_store.data() + key_index, 1});
1888 std::string
const value = fmtquill::vformat(
1889 "{}", fmtquill::basic_format_args<fmtquill::format_context>{_format_args_store.data() + value_index, 1});
1891 _mdc_fields.emplace_back(key, value);
1894 #if !defined(QUILL_NO_EXCEPTIONS) 1895 QUILL_CATCH(std::exception
const& e)
1902 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1907 for (
auto const& [key,
value] : _mdc_fields)
1909 mdc_state.set(key,
value);
1912 QUILL_TRY { mdc_state.rebuild_formatted_mdc(); }
1913 #if !defined(QUILL_NO_EXCEPTIONS) 1914 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
1917 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1921 else if (event == MacroMetadata::Event::MdcErase)
1923 format_args_decoder(read_pos, _format_args_store);
1925 QUILL_ASSERT(_format_args_store.size() >= 1,
1926 "MdcErase decoded argument count mismatch in BackendWorker::_apply_mdc_event()");
1928 if (thread_context._backend_mdc_state)
1931 _mdc_keys.reserve(static_cast<size_t>(_format_args_store.size()));
1935 for (
int i = 0; i < _format_args_store.size(); ++i)
1937 _mdc_keys.emplace_back(fmtquill::vformat(
1938 "{}", fmtquill::basic_format_args<fmtquill::format_context>{_format_args_store.data() + i, 1}));
1941 #if !defined(QUILL_NO_EXCEPTIONS) 1942 QUILL_CATCH(std::exception
const& e)
1949 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1954 for (std::string
const& key : _mdc_keys)
1956 thread_context._backend_mdc_state->erase(key);
1959 if (thread_context._backend_mdc_state->empty())
1961 thread_context._backend_mdc_state.reset();
1965 QUILL_TRY { thread_context._backend_mdc_state->rebuild_formatted_mdc(); }
1966 #if !defined(QUILL_NO_EXCEPTIONS) 1967 QUILL_CATCH(std::exception
const& e) { _notify_error(_options.
error_notifier, e.what()); }
1970 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
1976 else if (event == MacroMetadata::Event::MdcClear)
1978 thread_context._backend_mdc_state.reset();
1982 QUILL_ASSERT(
false,
"Unexpected MDC event in BackendWorker::_apply_mdc_event()");
1988 if (thread_context._backend_mdc_state)
1990 std::string_view
const mdc = thread_context._backend_mdc_state->formatted_mdc();
1991 transit_event->ensure_extra_data();
1992 transit_event->
extra_data->mdc.assign(mdc.data(), mdc.size());
1997 static void _format_and_split_arguments(std::vector<std::pair<std::string, std::string>>
const& orig_arg_names,
1998 std::vector<std::pair<std::string, std::string>>& named_args,
2002 for (
size_t i = 0; i < named_args.size(); ++i)
2004 if (i >= static_cast<size_t>(format_args_store.size()))
2009 std::string format_string{
"{}"};
2012 if ((i < orig_arg_names.size()) && !orig_arg_names[i].second.empty())
2014 format_string =
"{";
2015 format_string += orig_arg_names[i].second;
2016 format_string +=
"}";
2019 fmtquill::vformat_to(std::back_inserter(named_args[i].second), format_string,
2020 fmtquill::basic_format_args<fmtquill::format_context>{
2021 format_args_store.data() +
static_cast<std::ptrdiff_t
>(i),
2022 format_args_store.size() -
static_cast<int>(i)});
2026 sanitize_non_printable_chars(named_args[i].second, options);
2031 void _populate_formatted_named_args(
TransitEvent* transit_event,
2032 std::vector<std::pair<std::string, std::string>>
const& arg_names)
2034 transit_event->ensure_extra_data();
2036 auto* named_args = &transit_event->
extra_data->named_args;
2037 named_args->clear();
2039 if (arg_names.empty())
2044 named_args->resize(arg_names.size());
2047 for (
size_t i = 0; i < arg_names.size(); ++i)
2049 (*named_args)[i].first = arg_names[i].first;
2052 for (
size_t i = arg_names.size(); i < static_cast<size_t>(_format_args_store.size()); ++i)
2055 named_args->push_back(std::pair<std::string, std::string>(fmtquill::format(
"_{}", i), std::string{}));
2059 QUILL_TRY { _format_and_split_arguments(arg_names, *named_args, _format_args_store, _options); }
2060 #if !defined(QUILL_NO_EXCEPTIONS) 2061 QUILL_CATCH(std::exception
const&)
2069 _notify_error(_options.
error_notifier, std::string{
"Caught unhandled exception."});
2074 QUILL_ATTRIBUTE_HOT
void _populate_formatted_log_message(
TransitEvent* transit_event,
char const* message_format)
2076 transit_event->formatted_msg->clear();
2080 fmtquill::vformat_to(std::back_inserter(*transit_event->formatted_msg), message_format,
2081 fmtquill::basic_format_args<fmtquill::format_context>{
2082 _format_args_store.data(), _format_args_store.size()});
2086 sanitize_non_printable_chars(*transit_event->formatted_msg, _options);
2089 #if !defined(QUILL_NO_EXCEPTIONS) 2090 QUILL_CATCH(std::exception
const& e)
2092 transit_event->formatted_msg->clear();
2093 std::string
const error =
2094 fmtquill::format(R
"([Could not format log statement. message: "{}", location: "{}", error: "{}"])", 2095 transit_event->macro_metadata->message_format(), 2096 transit_event->macro_metadata->short_source_location(), e.what()); 2098 transit_event->formatted_msg->append(error); 2103 transit_event->formatted_msg->clear(); 2104 std::string const error = fmtquill::format(
2105 R
"([Could not format log statement. message: "{}", location: "{}", error: "{}"])", 2106 transit_event->macro_metadata->message_format(), 2107 transit_event->macro_metadata->short_source_location(), "Caught unhandled exception.");
2109 transit_event->formatted_msg->append(error);
2115 void _apply_runtime_metadata(std::byte*& read_pos,
TransitEvent* transit_event)
2119 char const*
function;
2122 if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy)
2129 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy)
2136 else if (transit_event->macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy)
2146 QuillError{
"Unexpected event type in _apply_runtime_metadata. This should never happen."});
2154 transit_event->ensure_extra_data();
2155 transit_event->
extra_data->runtime_metadata = temp;
2158 transit_event->macro_metadata = &transit_event->
extra_data->runtime_metadata.macro_metadata;
2161 template <
typename TFormattedMsg>
2162 static void sanitize_non_printable_chars(TFormattedMsg& formatted_msg,
BackendOptions const& options)
2165 bool contains_non_printable_char{
false};
2167 for (
char c : formatted_msg)
2171 contains_non_printable_char =
true;
2176 if (contains_non_printable_char)
2179 std::string
const formatted_msg_copy = {formatted_msg.data(), formatted_msg.size()};
2180 formatted_msg.clear();
2182 for (
char c : formatted_msg_copy)
2186 formatted_msg.push_back(c);
2191 unsigned char const byte =
static_cast<unsigned char>(c);
2192 constexpr
char hex[] =
"0123456789ABCDEF";
2193 formatted_msg.push_back(
'\\');
2194 formatted_msg.push_back(
'x');
2195 formatted_msg.push_back(hex[(byte >> 4) & 0xF]);
2196 formatted_msg.push_back(hex[byte & 0xF]);
2203 friend class quill::ManualBackendWorker;
2205 std::unique_ptr<BackendWorkerLock> _backend_worker_lock;
2207 SinkManager& _sink_manager = SinkManager::instance();
2210 uint64_t _last_output_timestamp{0};
2211 std::thread _worker_thread;
2214 std::vector<std::string> _removed_loggers;
2215 std::vector<ThreadContext*> _active_thread_contexts_cache;
2216 std::vector<Sink*> _active_sinks_cache;
2217 std::vector<std::pair<std::string, std::string>> _mdc_fields;
2218 std::vector<std::string> _mdc_keys;
2219 std::unordered_map<std::string, std::pair<std::string, std::vector<std::pair<std::string, std::string>>>> _named_args_templates;
2220 std::unordered_map<std::string, std::atomic<bool>*> _logger_removal_flags;
2221 std::string _named_args_format_template;
2222 std::string _process_id;
2223 std::chrono::steady_clock::time_point _last_rdtsc_resync_time;
2224 std::chrono::steady_clock::time_point _last_sink_flush_time;
2225 std::atomic<uint32_t> _worker_thread_id{0};
2226 std::atomic<bool> _is_worker_running{
false};
2227 std::atomic<bool> _has_worker_thread_exited{
true};
2229 alignas(QUILL_CACHE_LINE_ALIGNED) std::atomic<RdtscClock*> _rdtsc_clock{
2231 alignas(QUILL_CACHE_LINE_ALIGNED) std::mutex _wake_up_mutex;
2232 std::condition_variable _wake_up_cv;
2233 bool _wake_up_flag{
false};
2236 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 2237 #pragma warning(pop) QUILL_ATTRIBUTE_HOT void sleep_for_ns(uint64_t ns) noexcept
Mirrors std::this_thread::sleep_for(std::chrono::nanoseconds{ns}).
Definition: ThreadPrimitives.h:43
std::unique_ptr< ExtraData > extra_data
buffer for message
Definition: TransitEvent.h:271
A single-producer single-consumer FIFO circular buffer.
Definition: UnboundedSPSCQueue.h:43
void notify()
Wakes up the backend worker thread.
Definition: BackendWorker.h:300
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:257
Base class for sinks.
Definition: Sink.h:46
QUILL_ATTRIBUTE_COLD void run(BackendOptions const &options)
Starts the backend worker thread.
Definition: BackendWorker.h:141
bool enable_yield_when_idle
The backend employs "busy-waiting" by spinning around each frontend thread's queue.
Definition: BackendOptions.h:67
Definition: TransitEvent.h:33
~BackendWorker()
Destructor.
Definition: BackendWorker.h:97
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:115
Definition: ThreadContextManager.h:223
void(*)(std::byte *&data, DynamicFormatArgStore &args_store) FormatArgsDecoder
Decode functions.
Definition: Codec.h:415
Definition: BackendMdcState.h:21
std::function< void(std::string const &)> error_notifier
The backend may encounter exceptions that cannot be caught within user threads.
Definition: BackendOptions.h:241
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED std::string get_thread_name()
Returns the name of the thread.
Definition: ThreadUtilities.h:150
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:72
tm * localtime_rs(time_t const *timer, tm *buf)
Portable localtime_r or _s per operating system.
Definition: TimeUtilities.h:56
Definition: LogFunctions.h:269
QUILL_NODISCARD bool is_valid_logger() const noexcept
Checks if the logger is valid.
Definition: LoggerBase.h:115
void for_each_logger(TCallback cb) const
For backend use only.
Definition: LoggerManager.h:141
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:28
BackendWorker()=default
Constructor.
Codec contract notes (apply to this primary template and every specialization, including those in the...
Definition: Codec.h:168
Converts tsc ticks to nanoseconds since epoch.
Definition: RdtscClock.h:36
size_t transit_events_hard_limit
The backend gives priority to reading messages from the frontend queues and temporarily buffers them...
Definition: BackendOptions.h:115
QUILL_ATTRIBUTE_COLD void stop() noexcept
Stops the backend worker thread.
Definition: BackendWorker.h:248
std::array< std::string, LogLevelCount > log_level_descriptions
Holds descriptive names for various log levels used in logging operations.
Definition: BackendOptions.h:311
This class can be used when you want to run the backend worker on your own thread.
Definition: ManualBackendWorker.h:30
Definition: LoggerBase.h:39
custom exception
Definition: QuillError.h:47
QUILL_NODISCARD uint32_t get_backend_thread_id() const noexcept
Get the backend worker's thread id.
Definition: BackendWorker.h:132
bool ensure_monotonic_output_timestamps
Ensures sink-visible timestamps for regular log and metric records do not move backwards.
Definition: BackendOptions.h:189
std::string mdc_format_pattern
Format string used when rendering PatternFormatter's %(mdc).
Definition: BackendOptions.h:340
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:206
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:98
QUILL_ATTRIBUTE_HOT void yield_thread() noexcept
Mirrors std::this_thread::yield().
Definition: ThreadPrimitives.h:71
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.
Definition: UnboundedSPSCQueue.h:209
Definition: SinkManager.h:34
bool check_backend_singleton_instance
Enables a runtime check to detect multiple instances of the backend singleton.
Definition: BackendOptions.h:362
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t get_system_time_ns() noexcept
Mirrors std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::system_clock::now().time_since_epoch()).count().
Definition: ChronoTimeUtils.h:57
std::string thread_name
The name assigned to the backend, visible during thread naming queries (e.g., pthread_getname_np) or ...
Definition: BackendOptions.h:59
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:249
std::vector< uint16_t > cpu_affinity
Pins the backend to the specified CPUs.
Definition: BackendOptions.h:220
Definition: BackendWorker.h:80
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED uint32_t get_thread_id() noexcept
Returns the os assigned ID of the thread.
Definition: ThreadUtilities.h:213
Definition: LoggerManager.h:39
Configuration options for the backend.
Definition: BackendOptions.h:51
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:165
std::array< std::string, LogLevelCount > log_level_short_codes
Short codes or identifiers for each log level.
Definition: BackendOptions.h:321
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:304
Definition: ThreadContextManager.h:54
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:272
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:289