9 #include "quill/UserClockSource.h" 10 #include "quill/core/Attributes.h" 11 #include "quill/core/ChronoTimeUtils.h" 12 #include "quill/core/Codec.h" 13 #include "quill/core/Common.h" 14 #include "quill/core/FrontendOptions.h" 15 #include "quill/core/LogLevel.h" 16 #include "quill/core/LoggerBase.h" 17 #include "quill/core/MacroMetadata.h" 18 #include "quill/core/Rdtsc.h" 47 template <
typename TFrontendOptions>
51 using frontend_options_t = TFrontendOptions;
53 static constexpr
bool using_unbounded_queue =
54 (frontend_options_t::queue_type == QueueType::UnboundedBlocking) ||
55 (frontend_options_t::queue_type == QueueType::UnboundedDropping);
75 template <
bool enable_immediate_flush,
typename... Args>
79 macro_metadata->event() != MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy &&
80 macro_metadata->event() != MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy &&
81 macro_metadata->event() != MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy,
82 "log_statement() should not be called with MacroMetadata::Event::LogWithRuntimeMetadata");
84 QUILL_ASSERT(_valid.load(std::memory_order_acquire),
85 "Attempting to log with an invalidated logger");
89 uint64_t
const current_timestamp = _get_message_timestamp();
91 if (QUILL_UNLIKELY(_thread_context ==
nullptr))
94 _thread_context = detail::get_local_thread_context<frontend_options_t>();
98 size_t total_size =
sizeof(current_timestamp) + (
sizeof(uintptr_t) * 3) +
100 _thread_context->get_conditional_arg_size_cache(), fmt_args...);
102 std::byte* write_buffer = _reserve_queue_space(total_size, macro_metadata);
104 if (QUILL_UNLIKELY(write_buffer ==
nullptr))
111 #if defined(QUILL_ENABLE_ASSERTIONS) || !defined(NDEBUG) 112 std::byte
const*
const write_begin = write_buffer;
113 QUILL_ASSERT(write_begin,
"Reserved queue space returned nullptr in log_statement()");
117 write_buffer = _encode_header(write_buffer, current_timestamp, macro_metadata,
this,
118 detail::decode_and_store_args<detail::remove_cvref_t<Args>...>);
121 detail::encode(write_buffer, _thread_context->get_conditional_arg_size_cache(),
122 static_cast<decltype(fmt_args)&&
>(fmt_args)...);
124 QUILL_ASSERT_WITH_FMT(
125 write_buffer > write_begin,
126 "write_buffer must be greater than write_begin after encoding in log_statement(): msg=\"%s\"",
127 macro_metadata->message_format());
128 QUILL_ASSERT_WITH_FMT(
129 total_size == static_cast<size_t>(write_buffer - write_begin),
130 "Encoded bytes mismatch in log_statement(): total_size=%zu, actual_encoded=%zu, msg=\"%s\"",
131 total_size, static_cast<size_t>(write_buffer - write_begin), macro_metadata->message_format());
133 _commit_log_statement<enable_immediate_flush>(total_size);
155 template <
bool enable_immediate_flush,
typename... Args>
157 char const* fmt,
char const* file_path,
158 char const* function_name,
159 char const* tags, uint32_t line_number,
160 LogLevel log_level, Args&&... fmt_args)
162 QUILL_ASSERT(macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy ||
163 macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy ||
164 macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy,
165 "log_statement_runtime_metadata() should only be called with " 166 "MacroMetadata::Event::LogWithRuntimeMetadata");
168 QUILL_ASSERT(_valid.load(std::memory_order_acquire),
169 "Attempting to log with an invalidated logger");
173 uint64_t
const current_timestamp = _get_message_timestamp();
175 if (QUILL_UNLIKELY(_thread_context ==
nullptr))
178 _thread_context = detail::get_local_thread_context<frontend_options_t>();
183 size_t total_size{
sizeof(current_timestamp) + (
sizeof(uintptr_t) * 3)};
185 if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy)
188 _thread_context->get_conditional_arg_size_cache(), fmt, file_path, function_name, tags,
189 line_number, log_level, fmt_args...);
191 else if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy)
194 _thread_context->get_conditional_arg_size_cache(),
static_cast<void const*
>(fmt),
195 static_cast<void const*>(file_path),
static_cast<void const*
>(function_name),
196 static_cast<void const*>(tags), line_number, log_level, fmt_args...);
198 else if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy)
201 _thread_context->get_conditional_arg_size_cache(), fmt,
static_cast<void const*
>(file_path),
202 static_cast<void const*>(function_name), tags, line_number, log_level, fmt_args...);
209 std::byte* write_buffer = _reserve_queue_space(total_size, macro_metadata);
211 if (QUILL_UNLIKELY(write_buffer ==
nullptr))
218 #if defined(QUILL_ENABLE_ASSERTIONS) || !defined(NDEBUG) 219 std::byte
const*
const write_begin = write_buffer;
220 QUILL_ASSERT(write_begin,
221 "Reserved queue space returned nullptr in log_statement_runtime_metadata()");
225 write_buffer = _encode_header(write_buffer, current_timestamp, macro_metadata,
this,
226 detail::decode_and_store_args<detail::remove_cvref_t<Args>...>);
228 if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataDeepCopy)
231 detail::encode(write_buffer, _thread_context->get_conditional_arg_size_cache(), fmt, file_path, function_name,
232 tags, line_number, log_level,
static_cast<decltype(fmt_args)&&
>(fmt_args)...);
234 else if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataShallowCopy)
236 detail::encode(write_buffer, _thread_context->get_conditional_arg_size_cache(),
237 static_cast<void const*
>(fmt), static_cast<void const*>(file_path),
238 static_cast<void const*
>(function_name), static_cast<void const*>(tags),
239 line_number, log_level,
static_cast<decltype(fmt_args)&&
>(fmt_args)...);
241 else if (macro_metadata->event() == MacroMetadata::Event::LogWithRuntimeMetadataHybridCopy)
243 detail::encode(write_buffer, _thread_context->get_conditional_arg_size_cache(), fmt,
244 static_cast<void const*
>(file_path), static_cast<void const*>(function_name),
245 tags, line_number, log_level,
static_cast<decltype(fmt_args)&&
>(fmt_args)...);
248 QUILL_ASSERT_WITH_FMT(write_buffer > write_begin,
249 "write_buffer must be greater than write_begin after encoding in " 250 "log_statement_runtime_metadata(): fmt=\"%s\"",
252 QUILL_ASSERT_WITH_FMT(total_size == static_cast<size_t>(write_buffer - write_begin),
253 "Encoded bytes mismatch in log_statement_runtime_metadata(): " 254 "total_size=%zu, actual_encoded=%zu, fmt=\"%s\"",
255 total_size, static_cast<size_t>(write_buffer - write_begin), fmt);
257 _commit_log_statement<enable_immediate_flush>(total_size);
269 void init_backtrace(uint32_t max_capacity, LogLevel flush_level = LogLevel::None)
273 "",
"",
"{}",
nullptr, LogLevel::Critical, MacroMetadata::Event::InitBacktrace};
277 while (!this->
template log_statement<false>(¯o_metadata, max_capacity))
279 std::this_thread::sleep_for(std::chrono::nanoseconds{100});
283 _backtrace_flush_level.store(flush_level, std::memory_order_relaxed);
293 "",
"",
"",
nullptr, LogLevel::Critical, MacroMetadata::Event::FlushBacktrace};
296 while (!this->
template log_statement<false>(¯o_metadata))
298 std::this_thread::sleep_for(std::chrono::nanoseconds{100});
322 "",
"",
"",
nullptr, LogLevel::Critical, MacroMetadata::Event::Flush};
324 std::atomic<bool> backend_thread_flushed{
false};
325 std::atomic<bool>* backend_thread_flushed_ptr = &backend_thread_flushed;
328 while (!this->log_statement<false>(¯o_metadata, reinterpret_cast<uintptr_t>(backend_thread_flushed_ptr)))
330 if (sleep_duration_ns > 0)
332 std::this_thread::sleep_for(std::chrono::nanoseconds{sleep_duration_ns});
336 std::this_thread::yield();
341 while (!backend_thread_flushed.load())
343 if (sleep_duration_ns > 0)
345 std::this_thread::sleep_for(std::chrono::nanoseconds{sleep_duration_ns});
349 std::this_thread::yield();
360 LoggerImpl(std::string logger_name, std::vector<std::shared_ptr<Sink>> sinks,
364 static_cast<std::string&&>(logger_name),
static_cast<std::vector<std::shared_ptr<Sink>
>&&>(sinks),
365 static_cast<PatternFormatterOptions&&>(pattern_formatter_options), clock_source, user_clock)
367 if (this->_user_clock)
370 this->_clock_source = ClockSourceType::User;
378 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t _get_message_timestamp()
const 383 if (_clock_source == ClockSourceType::Tsc)
388 if (_clock_source == ClockSourceType::System)
390 return detail::get_timestamp_ns<std::chrono::system_clock>();
395 return _user_clock->now();
408 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::byte* _reserve_queue_space(
size_t total_size,
411 std::byte* write_buffer =
412 _thread_context->get_spsc_queue<frontend_options_t::queue_type>().prepare_write(total_size);
414 if constexpr ((frontend_options_t::queue_type == QueueType::BoundedDropping) ||
415 (frontend_options_t::queue_type == QueueType::UnboundedDropping))
417 if (QUILL_UNLIKELY(write_buffer ==
nullptr))
420 if (macro_metadata->event() == MacroMetadata::Event::Log)
422 _thread_context->increment_failure_counter();
426 else if constexpr ((frontend_options_t::queue_type == QueueType::BoundedBlocking) ||
427 (frontend_options_t::queue_type == QueueType::UnboundedBlocking))
429 if (QUILL_UNLIKELY(write_buffer ==
nullptr))
431 if (macro_metadata->event() == MacroMetadata::Event::Log)
433 _thread_context->increment_failure_counter();
438 if constexpr (frontend_options_t::blocking_queue_retry_interval_ns > 0)
440 std::this_thread::sleep_for(std::chrono::nanoseconds{frontend_options_t::blocking_queue_retry_interval_ns});
445 _thread_context->get_spsc_queue<frontend_options_t::queue_type>().prepare_write(total_size);
446 }
while (write_buffer ==
nullptr);
457 template <
bool enable_immediate_flush>
458 QUILL_ATTRIBUTE_HOT
void _commit_log_statement(
size_t total_size)
460 _thread_context->get_spsc_queue<frontend_options_t::queue_type>().finish_and_commit_write(total_size);
462 if constexpr (enable_immediate_flush)
464 uint32_t
const threshold = _message_flush_threshold.load(std::memory_order_relaxed);
465 if (QUILL_UNLIKELY(threshold != 0))
467 uint32_t
const prev = _messages_since_last_flush.fetch_add(1, std::memory_order_relaxed);
468 if ((prev + 1) >= threshold)
470 _messages_since_last_flush.store(0, std::memory_order_relaxed);
485 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT
static std::byte* _encode_header(std::byte* write_buffer, uint64_t timestamp,
490 std::memcpy(write_buffer, ×tamp,
sizeof(timestamp));
491 write_buffer +=
sizeof(timestamp);
493 std::memcpy(write_buffer, &metadata,
sizeof(uintptr_t));
494 write_buffer +=
sizeof(uintptr_t);
496 std::memcpy(write_buffer, &logger_ctx,
sizeof(uintptr_t));
497 write_buffer +=
sizeof(uintptr_t);
499 std::memcpy(write_buffer, &decoder,
sizeof(uintptr_t));
500 write_buffer +=
sizeof(uintptr_t);
void init_backtrace(uint32_t max_capacity, LogLevel flush_level=LogLevel::None)
Init a backtrace for this logger.
Definition: Logger.h:269
Base class for sinks.
Definition: Sink.h:40
void(*)(std::byte *&data, DynamicFormatArgStore &args_store) FormatArgsDecoder
Decode functions.
Definition: Codec.h:399
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT size_t compute_encoded_size_and_cache_string_lengths(QUILL_MAYBE_UNUSED SizeCacheVector &conditional_arg_size_cache, Args const &... args)
Calculates the total size required to encode the provided arguments.
Definition: Codec.h:354
void flush_backtrace()
Dump any stored backtrace messages.
Definition: Logger.h:289
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
QUILL_ATTRIBUTE_HOT bool log_statement(MacroMetadata const *macro_metadata, Args &&... fmt_args)
Push a log message to the spsc queue to be logged by the backend thread.
Definition: Logger.h:76
Thread safe logger.
Definition: Logger.h:48
QUILL_ATTRIBUTE_HOT bool log_statement_runtime_metadata(MacroMetadata const *macro_metadata, char const *fmt, char const *file_path, char const *function_name, char const *tags, uint32_t line_number, LogLevel log_level, Args &&... fmt_args)
Push a log message with runtime metadata to the spsc queue to be logged by the backend thread...
Definition: Logger.h:156
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t rdtsc() noexcept
Get the TSC counter.
Definition: Rdtsc.h:109
Definition: LoggerBase.h:35
QUILL_ATTRIBUTE_HOT void encode(std::byte *&buffer, SizeCacheVector const &conditional_arg_size_cache, Args &&... args)
Encodes multiple arguments into a buffer.
Definition: Codec.h:381
Base class that provides a timestamp for log statements based on a user-provided clock source...
Definition: UserClockSource.h:25
void flush_log(uint32_t sleep_duration_ns=100)
Blocks the calling thread until all log messages up to the current timestamp are flushed.
Definition: Logger.h:319
Definition: BackendWorker.h:77
Definition: LoggerManager.h:33