quill
Public Attributes | List of all members
BackendOptions Struct Reference

Configuration options for the backend. More...

#include <BackendOptions.h>

Collaboration diagram for BackendOptions:
Collaboration graph
[legend]

Public Attributes

std::string thread_name = "QuillBackend"
 The name assigned to the backend, visible during thread naming queries (e.g., pthread_getname_np) or in the debugger.
 
bool enable_yield_when_idle = false
 The backend employs "busy-waiting" by spinning around each frontend thread's queue. More...
 
std::chrono::nanoseconds sleep_duration = std::chrono::nanoseconds{500}
 Specifies the duration the backend sleeps if there is no remaining work to process in the queues.
 
uint32_t transit_event_buffer_initial_capacity = 128
 The backend pops all log messages from the frontend queues and buffers them in a local ring buffer queue as transit events. More...
 
size_t transit_events_soft_limit = 4096
 The backend gives priority to reading messages from the frontend queues of all the hot threads and temporarily buffers them. More...
 
size_t transit_events_hard_limit = 32'768
 The backend gives priority to reading messages from the frontend queues and temporarily buffers them. More...
 
std::chrono::microseconds log_timestamp_ordering_grace_period {1}
 The backend iterates through all frontend lock-free queues and pops all messages from each queue. More...
 
bool wait_for_queues_to_empty_before_exit = true
 When this option is enabled and the application is terminating, the backend worker thread will not exit until all the frontend queues are empty. More...
 
uint16_t cpu_affinity = (std::numeric_limits<uint16_t>::max)()
 Pins the backend to the specified CPU. More...
 
std::function< void(std::string const &)> error_notifier
 The backend may encounter exceptions that cannot be caught within user threads. More...
 
std::chrono::milliseconds rdtsc_resync_interval = std::chrono::milliseconds{500}
 This option is only applicable if at least one frontend is using a Logger with ClockSourceType::Tsc. More...
 
std::chrono::milliseconds sink_min_flush_interval = std::chrono::milliseconds{200}
 This option specifies the minimum time interval (in milliseconds) before the backend thread flushes the output buffers (flush_sink()) for all sinks in the application. More...
 
std::function< bool(char c)> check_printable_char
 This option enables a check that verifies the log message contains only printable characters before forwarding it to the sinks. More...
 
std::array< std::string, 11 > log_level_descriptions
 Holds descriptive names for various log levels used in logging operations. More...
 
std::array< std::string, 11 > log_level_short_codes
 Short codes or identifiers for each log level. More...
 
bool check_backend_singleton_instance = true
 Enables a runtime check to detect multiple instances of the backend singleton. More...
 

Detailed Description

Configuration options for the backend.

This struct defines settings for the backend thread

Member Data Documentation

◆ check_backend_singleton_instance

bool BackendOptions::check_backend_singleton_instance = true

Enables a runtime check to detect multiple instances of the backend singleton.

When mixing shared and static libraries, linkage issues can lead to multiple instances of the backend singleton. This may result in multiple backend worker threads running simultaneously, causing unexpected behavior or crashes.

This issue commonly occurs on Windows when Quill is compiled as a static library and linked into both a shared library and the main executable, creating separate instances. While using Quill as a static library is generally recommended, in such cases, the preferred approach is to build Quill as a shared library and export its symbols (e.g., using WINDOWS_EXPORT_ALL_SYMBOLS).

On Windows, this check is implemented using a named mutex, whereas on Linux and other POSIX systems, it relies on a named semaphore. In rare cases, this mechanism may interfere with certain environments or containerized deployments. If necessary, this check can be disabled by setting this option to false.

Setting this option to true enables the check, while setting it to false disables it.

◆ check_printable_char

std::function<bool(char c)> BackendOptions::check_printable_char
Initial value:
= [](char c)
{ return (c >= ' ' && c <= '~') || (c == '\n'); }

This option enables a check that verifies the log message contains only printable characters before forwarding it to the sinks.

This adds an extra layer of safety by filtering out non-printable characters from the log file. Any non-printable characters are converted to their equivalent hex value.

The check applies only when at least one argument in a log statement is of type string.

You can customize this callback to define your own range of printable characters if needed.

To disable this check, you can provide: std::function<bool(char c)> check_printable_char = {}

◆ cpu_affinity

uint16_t BackendOptions::cpu_affinity = (std::numeric_limits<uint16_t>::max)()

Pins the backend to the specified CPU.

By default, the backend is not pinned to any CPU unless a value is specified. It is recommended to pin the backend to a shared non-critical CPU. Use std::numeric_limits<uint16_t>::max() as an undefined value to avoid setting CPU affinity.

◆ enable_yield_when_idle

bool BackendOptions::enable_yield_when_idle = false

The backend employs "busy-waiting" by spinning around each frontend thread's queue.

If enabled, the backend will yield when there is no remaining work, potentially reducing the OS scheduler priority for the backend. This option is effective only when sleep_duration is set to 0.

◆ error_notifier

std::function<void(std::string const&)> BackendOptions::error_notifier
Initial value:
= [](std::string const& error_message)
{
std::fprintf(stderr, "%s\n", error_message.data());
}

The backend may encounter exceptions that cannot be caught within user threads.

In such cases, the backend invokes this callback to notify the user.

This function sets up a user error notifier to handle backend errors and notifications, such as when the unbounded queue reallocates or when the bounded queue becomes full.

To disable notifications, simply leave the function undefined: std::function<void(std::string const&)> backend_error_notifier = {};

It's safe to perform logging operations within this function (e.g., LOG_INFO(...)), but avoid calling logger->flush_log(). The function is invoked on the backend thread, which should not remain in a waiting state as it waits for itself.

◆ log_level_descriptions

std::array<std::string, 11> BackendOptions::log_level_descriptions
Initial value:
= {
"TRACE_L3", "TRACE_L2", "TRACE_L1", "DEBUG", "INFO", "NOTICE",
"WARNING", "ERROR", "CRITICAL", "BACKTRACE", "NONE"}

Holds descriptive names for various log levels used in logging operations.

The indices correspond to LogLevel enum values defined elsewhere in the codebase. These names provide human-readable identifiers for each log level.

◆ log_level_short_codes

std::array<std::string, 11> BackendOptions::log_level_short_codes
Initial value:
= {"T3", "T2", "T1", "D", "I", "N",
"W", "E", "C", "BT", "_"}

Short codes or identifiers for each log level.

Provides short codes representing each log level for compact identification and usage. The indices correspond to LogLevel enum values defined elsewhere in the codebase.

◆ log_timestamp_ordering_grace_period

std::chrono::microseconds BackendOptions::log_timestamp_ordering_grace_period {1}

The backend iterates through all frontend lock-free queues and pops all messages from each queue.

It then buffers and logs the message with the lowest timestamp among them.

Each frontend lock-free queue corresponds to a thread, and when multiple frontend threads are pushing logs simultaneously, it is possible to read a timestamp from the last queue in the iteration but miss that timestamp when the first queue was read because it was not available at that time.

When this option is set to a non-zero value, the backend takes a timestamp (now()) before reading the queues. It uses that timestamp to ensure that each log message's timestamp from the frontend queues is less than or equal to the stored now() timestamp minus the specified grace period, guaranteeing ordering by timestamp.

Messages that fail the above check remain in the lock-free queue. They are checked again in the next iteration. The timestamp check is performed with microsecond precision.

Example scenario:

  1. Frontend thread takes a timestamp at the very start of logging, then becomes delayed (preempted, blocked, processing slowly, etc.) before pushing to the queue.
  2. Backend thread takes timestamp now() and subtracts the grace period, reads queues up to the adjusted now(), and writes the logs.
  3. Frontend thread wakes up and pushes the message with its already-recorded timestamp to the queue.
  4. Backend thread reads and writes the delayed timestamp, resulting in an out-of-order log.

Setting this option to a non-zero value causes a minor delay in reading the messages from the lock-free queues but ensures correct timestamp order.

Setting log_timestamp_ordering_grace_period to zero disables strict timestamp ordering.

◆ rdtsc_resync_interval

std::chrono::milliseconds BackendOptions::rdtsc_resync_interval = std::chrono::milliseconds{500}

This option is only applicable if at least one frontend is using a Logger with ClockSourceType::Tsc.

When the system clock is used, this option can be ignored.

Controls the frequency at which the backend recalculates and syncs the internal RdtscClock with the system time from the system wall clock. The TSC clock can drift slightly over time and is not synchronized with NTP server updates.

Smaller values provide more accurate log timestamps at the cost of additional system clock calls. Changing this value only affects the performance of the backend worker.

◆ sink_min_flush_interval

std::chrono::milliseconds BackendOptions::sink_min_flush_interval = std::chrono::milliseconds{200}

This option specifies the minimum time interval (in milliseconds) before the backend thread flushes the output buffers (flush_sink()) for all sinks in the application.

The backend thread will ensure that no sink is flushed more frequently than this interval. Explicit calls to logger->flush_log() override this interval and trigger an immediate flush.

However, if the backend thread is actively processing messages, flushing may occur less frequently than the specified interval.

Setting this value to 0 disables the feature, causing the backend thread to flush sinks whenever there is no pending work, provided a write to the sink has occurred.

This setting applies globally and affects all sinks in the application.

◆ transit_event_buffer_initial_capacity

uint32_t BackendOptions::transit_event_buffer_initial_capacity = 128

The backend pops all log messages from the frontend queues and buffers them in a local ring buffer queue as transit events.

The transit_event_buffer is unbounded, starting with a customizable initial capacity (in items, not bytes) and will reallocate up to transit_events_hard_limit The backend will use a separate transit_event_buffer for each frontend thread. The capacity must be a power of two.

◆ transit_events_hard_limit

size_t BackendOptions::transit_events_hard_limit = 32'768

The backend gives priority to reading messages from the frontend queues and temporarily buffers them.

If a frontend thread continuously push messages to the queue (e.g., logging in a loop), no logs can ever be processed.

As the backend buffers messages, it can keep buffering indefinitely if the frontend threads keep pushing.

This limit is the maximum size of the backend event buffer. When reached, the backend will stop reading the frontend queues until there is space available in the buffer.

Note
This number represents a limit PER frontend threads.

◆ transit_events_soft_limit

size_t BackendOptions::transit_events_soft_limit = 4096

The backend gives priority to reading messages from the frontend queues of all the hot threads and temporarily buffers them.

If a frontend threads continuously push messages to the queue (e.g., logging in a loop), no logs can ever be processed.

When the soft limit is reached the backend worker thread will try to process a batch of cached transit events all at once

The frontend queues are emptied on each iteration, so the actual popped messages can be much greater than the transit_events_soft_limit.

Note
This number represents a limit across the messages received from ALL frontend threads.

◆ wait_for_queues_to_empty_before_exit

bool BackendOptions::wait_for_queues_to_empty_before_exit = true

When this option is enabled and the application is terminating, the backend worker thread will not exit until all the frontend queues are empty.

However, if there is a thread during application destruction that keeps trying to log indefinitely, the backend will be unable to exit because it keeps popping log messages.

When this option is disabled, the backend will try to read the queues once and then exit. Reading the queues only once means that some log messages can be dropped, especially when strict_log_timestamp_order is set to true.


The documentation for this struct was generated from the following file: