API Reference

Log Macros

Basic Macros

The following macros log without reading the time of day at the point of logging, instead it is read when the log message is received by the background thread. Use these macros if a loss of timestamp accuracy is acceptable to gain performance.

XTR_LOG(SINK, ...)

Basic log macro, logs the specified format string and arguments to the given sink, blocking if the sink is full. Timestamps are read in the background thread—if this is undesirable use XTR_LOG_RTC or XTR_LOG_TSC which read timestamps at the point of logging. This macro will log regardless of the sink’s log level.

XTR_LOGL(LEVEL, SINK, ...)

Log level variant of XTR_LOG. If the specified log level has lower importance than the log level of the sink, then the message is dropped (please see the log levels section of the user guide for details).

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

Note

If the ‘fatal’ level is passed then the log message is written, xtr::sink::sync is invoked, then the program is terminated via abort(3).

Note

Log statements with the ‘debug’ level can be disabled at build time by defining XTR_NDEBUG.

XTR_TRY_LOG(SINK, ...)

Non-blocking variant of XTR_LOG. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

XTR_TRY_LOGL(LEVEL, SINK, ...)

Non-blocking variant of XTR_LOGL. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

Time-Stamp Counter Timestamped Macros

The following macros log and read the time-stamp counter at the point of logging. Use these macros if the most accurate timestamps are required. On some systems reading rdtsc may be slower than using the real-time clock (see below).

XTR_LOG_TSC(SINK, ...)

Timestamped log macro, logs the specified format string and arguments to the given sink along with a timestamp obtained by reading the CPU timestamp counter via the RDTSC instruction. The non-blocking variant of this macro is XTR_TRY_LOG_TSC which will discard the message if the sink is full. This macro will log regardless of the sink’s log level.

XTR_LOGL_TSC(LEVEL, SINK, ...)

Log level variant of XTR_LOG_TSC. If the specified log level has lower importance than the log level of the sink, then the message is dropped (please see the log levels section of the user guide for details).

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

Note

If the ‘fatal’ level is passed then the log message is written, xtr::sink::sync is invoked, then the program is terminated via abort(3).

Note

Log statements with the ‘debug’ level can be disabled at build time by defining XTR_NDEBUG.

XTR_TRY_LOG_TSC(SINK, ...)

Non-blocking variant of XTR_LOG_TSC. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

XTR_TRY_LOGL_TSC(LEVEL, SINK, ...)

Non-blocking variant of XTR_TRY_LOGL_TSC. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

Real-Time Clock Timestamped Macros

The following macros log and read the system real-time clock at the point of logging. Use these macros if accurate timestamps are required.

XTR_LOG_RTC(SINK, ...)

Timestamped log macro, logs the specified format string and arguments to the given sink along with a timestamp obtained by invoking clock_gettime(3) with a clock source of CLOCK_REALTIME_COARSE on Linux or CLOCK_REALTIME_FAST on FreeBSD. Depending on the host CPU this may be faster than XTR_LOG_TSC. The non-blocking variant of this macro is XTR_TRY_LOG_RTC which will discard the message if the sink is full. This macro will log regardless of the sink’s log level.

XTR_LOGL_RTC(LEVEL, SINK, ...)

Log level variant of XTR_LOG_RTC. If the specified log level has lower importance than the log level of the sink, then the message is dropped (please see the log levels section of the user guide for details).

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

Note

If the ‘fatal’ level is passed then the log message is written, xtr::sink::sync is invoked, then the program is terminated via abort(3).

Note

Log statements with the ‘debug’ level can be disabled at build time by defining XTR_NDEBUG.

XTR_TRY_LOG_RTC(SINK, ...)

Non-blocking variant of XTR_LOG_RTC. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

XTR_TRY_LOGL_RTC(LEVEL, SINK, ...)

Non-blocking variant of XTR_TRY_LOGL_RTC. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

User-Supplied Timestamp Macros

The following macros log and accept a user-supplied timestamp.

XTR_LOG_TS(SINK, TS, ...)

User-supplied timestamp log macro, logs the specified format string and arguments to the given sink along with the specified timestamp, blocking if the sink is full. The timestamp may be any type as long as it has a formatter defined—please see the custom formatters section of the user guide for details. xtr::timespec is provided as a convenience type which is compatible with std::timespec and has a formatter pre-defined. A formatter for std::timespec isn’t defined in order to avoid conflict with user code that also defines such a formatter. This macro will log regardless of the sink’s log level.

  • TS: The timestamp to apply to the log statement.

XTR_LOGL_TS(LEVEL, SINK, TS, ...)

Log level variant of XTR_LOG_TS. If the specified log level has lower importance than the log level of the sink, then the message is dropped (please see the log levels section of the user guide for details).

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

  • TS: The timestamp to apply to the log statement.

Note

If the ‘fatal’ level is passed then the log message is written, xtr::sink::sync is invoked, then the program is terminated via abort(3).

Note

Log statements with the ‘debug’ level can be disabled at build time by defining XTR_NDEBUG.

XTR_TRY_LOG_TS(SINK, TS, ...)

Non-blocking variant of XTR_LOG_TS. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

XTR_TRY_LOGL_TS(LEVEL, SINK, TS, ...)

Non-blocking variant of XTR_TRY_LOGL_TS. The message will be discarded if the sink is full. If a message is dropped a warning will appear in the log.

  • LEVEL: The unqualified log level name, for example simply “info” or “error”.

  • TS: The timestamp to apply to the log statement.

Logger

class logger

The main logger class. When constructed a background thread will be created which is used for formatting log messages and performing I/O. To write to the logger call logger::get_sink to create a sink, then pass the sink to a macro such as XTR_LOG (see the creating and writing to sinks section of the user guide for details).

Public Functions

template<typename Clock = std::chrono::system_clock>
inline explicit logger(const char *path, Clock &&clock = Clock(), std::string command_path = default_command_path(), log_level_style_t level_style = default_log_level_style)

Path constructor. The first argument is the path to a file which should be opened and logged to. The file will be opened in append mode, and will be created if it does not exist. Errors will be written to stdout.

  • path: The path of a file to write log statements to.

  • clock: A function returning the current time of day as a std::timespec. This function will be invoked when creating timestamps for log statements produced by the basic log macros— please see the basic time source section of the user guide for details. The default clock is std::chrono::system_clock.

  • command_path: The path where the local domain socket used to communicate with xtrctl should be created. The default behaviour is to create sockets in $XDG_RUNTIME_DIR (if set, otherwise “/run/user/<uid>”). If that directory does not exist or is inaccessible then $TMPDIR (if set, otherwise “/tmp”) will be used instead. See default_command_path for further details. To prevent a socket from being created, pass null_command_path.

  • level_style: The log level style that will be used to prefix each log statement—please refer to the log_level_style_t documentation for details.

template<typename Clock = std::chrono::system_clock>
inline explicit logger(FILE *stream = stderr, Clock &&clock = Clock(), std::string command_path = default_command_path(), log_level_style_t level_style = default_log_level_style)

Stream constructor.

It is expected that this constructor will be used with streams such as stdout or stderr. If a stream that has been opened by the user is to be passed to the logger then the stream constructor with reopen path constructor is recommended instead, as this will mean that the log file can be rotated—please refer to the xtrctl documentation for the reopening log files command for details.

  • stream: The stream to write log statements to.

  • clock: Please refer to the description above.

  • command_path: Please refer to the description above.

  • level_style: The log level style that will be used to prefix each log statement—please refer to the log_level_style_t documentation for details.

Note

Reopening the log file via the xtrctl tool is not supported if this constructor is used.

template<typename Clock = std::chrono::system_clock>
inline logger(std::string reopen_path, FILE *stream, Clock &&clock = Clock(), std::string command_path = default_command_path(), log_level_style_t level_style = default_log_level_style)

Stream constructor with reopen path.

  • reopen_path: The path of the file associated with the stream argument. This path will be used to reopen the stream if requested via the xtrctl reopen command. Pass null_reopen_path if no filename is associated with the stream.

  • stream: The stream to write log statements to.

  • clock: Please refer to the description above.

  • command_path: Please refer to the description above.

  • level_style: The log level style that will be used to prefix each log statement—please refer to the log_level_style_t documentation for details.

Note

Reopening the log file via the xtrctl tool is supported, with the reopen_path argument specifying the path to reopen.

template<typename Clock = std::chrono::system_clock>
inline explicit logger(storage_interface_ptr storage, Clock &&clock = Clock(), std::string command_path = default_command_path(), log_level_style_t level_style = default_log_level_style)

Custom back-end constructor (please refer to the custom back-ends section of the user guide for further details on implementing a custom back-end).

  • storage: Unique pointer to an object implementing the storage_interface interface. The logger will invoke methods on this object from the background thread in order to write log data to whatever underlying storage medium is implemented by the object, such as disk, network, dot-matrix printer etc.

  • clock: Please refer to the description above.

  • command_path: Please refer to the description above.

  • level_style: The log level style that will be used to prefix each log statement—please refer to the log_level_style_t documentation for details.

~logger() = default

Logger destructor. This function will join the consumer thread. If sinks are still connected to the logger then the consumer thread will not terminate until the sinks disconnect, i.e. the destructor will block until all connected sinks disconnect from the logger.

inline std::thread::native_handle_type consumer_thread_native_handle()

Returns the native handle for the logger’s consumer thread. This may be used for setting thread affinities or other thread attributes.

sink get_sink(std::string name)

Creates a sink with the specified name. Note that each call to this function creates a new sink; if repeated calls are made with the same name, separate sinks with the name name are created.

Parameters:

name – The name for the given sink.

void register_sink(sink &s, std::string name) noexcept

Registers the sink with the logger. Note that the sink name does not need to be unique; if repeated calls are made with the same name, separate sinks with the same name are registered.

Parameters:
  • s – The sink to register.

  • name – The name for the given sink.

Pre:

The sink must be closed.

void set_command_path(std::string path) noexcept

Sets the logger command path—please refer to the ‘command_path’ argument description above for details.

void set_log_level_style(log_level_style_t level_style) noexcept

Sets the logger log level style—please refer to the log_level_style_t documentation for details.

void set_default_log_level(log_level_t level)

Sets the default log level. Sinks created via future calls to get_sink will be created with the given log level.

Sink

class sink

Log sink class. A sink is how log messages are written to a log. Each sink has its own queue which is used to send log messages to the logger. Sink operations are not thread safe, with the exception of set_level and level.

It is expected that an application will have many sinks, such as a sink per thread or sink per component. A sink that is connected to a logger may be created by calling logger::get_sink. A sink that is not connected to a logger may be created simply by default construction, then the sink may be connected to a logger by calling logger::register_sink.

Public Functions

sink(const sink &other)

Sink copy constructor. When a sink is copied it is automatically registered with the same logger object as the source sink, using the same sink name. The sink name may be modified by calling set_name.

sink &operator=(const sink &other)

Sink copy assignment operator. When a sink is copy assigned it is closed in order to disconnect it from any existing logger object and is then automatically registered with the same logger object as the source sink, using the same sink name. The sink name may be modified by calling set_name.

~sink()

Sink destructor. When a sink is destructed it is automatically closed.

void close()

Closes the sink. After this function returns the sink is closed and log() functions may not be called on the sink. The sink may be re-opened by calling logger::register_sink.

bool is_open() const noexcept

Returns true if the sink is open (connected to a logger), or false if the sink is closed (not connected to a logger). Log messages may only be written to a sink that is open.

void sync()

Synchronizes all log calls previously made by this sink with the background thread and syncs all data to back-end storage.

Post:

All entries in the sink’s queue have been processed by the background thread, buffers have been flushed and the sync() function on the storage interface has been called. For the default (disk) storage this means fsync(2) (if available) has been called.

void set_name(std::string name)

Sets the sink’s name to the specified value.

template<auto Format, auto Level, typename Tags = void(), typename ...Args>
void log(Args&&... args) noexcept((XTR_NOTHROW_INGESTIBLE(Args, args) && ...))

Logs the given format string and arguments. This function is not intended to be used directly, instead one of the XTR_LOG macros should be used. It is provided for use in situations where use of a macro may be undesirable.

inline void set_level(log_level_t level)

Sets the log level of the sink to the specified level (see log_level_t). Any log statement made with a log level with lower importance than the current level will be dropped—please see the log levels section of the user guide for details.

inline log_level_t level() const

Returns the current log level (see log_level_t).

inline std::size_t capacity() const

Returns the capacity (in bytes) of the queue that the sink uses to send log data to the background thread. To override the sink capacity set XTR_SINK_CAPACITY in xtr/config.hpp.

Nocopy

template<typename T>
inline auto xtr::nocopy(const T &arg)

nocopy is used to specify that a log argument should be passed by reference instead of by value, so that arg becomes nocopy(arg). Note that by default, all strings including C strings and std::string_view are copied. In order to pass strings by reference they must be wrapped in a call to nocopy. Please see the passing arguments by value or reference and string arguments sections of the user guide for further details.

Log Levels

enum class xtr::log_level_t

Passed to XTR_LOGL, XTR_LOGL_TSC etc to indicate the severity of the log message.

Values:

enumerator none
enumerator fatal
enumerator error
enumerator warning
enumerator info
enumerator debug

If the none level is applied to a sink then all log statements will be disabled. Fatal log statements will still call abort(3), however.

log_level_t xtr::log_level_from_string(std::string_view str)

Converts a string containing a log level name to the corresponding log_level_t enum value. Throws std::invalid_argument if the given string does not correspond to any log level.

Log Level Styles

using xtr::log_level_style_t = const char *(*)(log_level_t)

Log level styles are used to customise the formatting used when prefixing log statements with their associated log level (see log_level_t). Styles are simply function pointers—to provide a custom style, define a function returning a string literal and accepting a single argument of type log_level_t and pass the function to logger::logger or logger::set_log_level_style. The values returned by the function will be prefixed to log statements produced by the logger. Two formatters are provided, the default formatter default_log_level_style and a Systemd compatible style systemd_log_level_style.

const char *xtr::default_log_level_style(log_level_t level)

The default log level style (see log_level_style_t). Returns a single upper-case character representing the log level followed by a space, e.g. “E “, “W “, “I “ for log_level_t::error, log_level_t::warning, log_level_t::info and so on.

const char *xtr::systemd_log_level_style(log_level_t level)

Systemd log level style (see log_level_style_t). Returns strings as described in sd-daemon(3), e.g. “<0>”, “<1>”, “<2>” etc.

Storage Interfaces

struct storage_interface

Interface allowing custom back-ends to be implemented. To create a custom back-end, inherit from storage_interfance, implement all pure-virtual functions then pass a storage_interface_ptr pointing to an instance of the custom back-end to logger::logger.

Public Functions

virtual std::span<char> allocate_buffer() = 0

Allocates a buffer for formatted log data to be written to. Once a buffer has been allocated, allocate_buffer will not be called again until the buffer has been submitted via submit_buffer.

virtual void submit_buffer(char *buf, std::size_t size) = 0

Submits a buffer containing formatted log data to be written.

virtual void flush() = 0

Invoked to indicate that the back-end should write any buffered data to its associated backing store.

virtual void sync() noexcept = 0

Invoked to indicate that the back-end should ensure that all data written to the associated backing store has reached permanent storage.

virtual int reopen() noexcept = 0

Invoked to indicate that if the back-end has a regular file opened for writing log data then the file should be reopened.

using xtr::storage_interface_ptr = std::unique_ptr<storage_interface>

Convenience typedef for std::unique_ptr<storage_interface>

class io_uring_fd_storage : public detail::fd_storage_base

An implementation of storage_interface that uses io_uring(7) to perform file I/O (Linux only).

Public Functions

explicit io_uring_fd_storage(int fd, std::string reopen_path = null_reopen_path, std::size_t buffer_capacity = default_buffer_capacity, std::size_t queue_size = default_queue_size, std::size_t batch_size = default_batch_size)

File descriptor constructor.

  • fd: File descriptor to write to. This will be duplicated via a call to dup(2), so callers may close the file descriptor immediately after this constructor returns if desired.

  • reopen_path: The path of the file associated with the fd argument. This path will be used to reopen the file if requested via the xtrctl reopen command. Pass null_reopen_path if no filename is associated with the file descriptor.

  • buffer_capacity: The size in bytes of a single io_uring buffer.

  • queue_size: The size of the io_uring submission queue.

  • batch_size: The number of buffers to collect before submitting the buffers to io_uring. If XTR_IO_URING_POLL is set to 1 in xtr/config.hpp then this parameter has no effect.

Public Static Attributes

static constexpr std::size_t default_buffer_capacity = 64 * 1024

Default value for the buffer_capacity constructor argument.

static constexpr std::size_t default_queue_size = 1024

Default value for the queue_size constructor argument.

static constexpr std::size_t default_batch_size = 32

Default value for the batch_size constructor argument.

class posix_fd_storage : public detail::fd_storage_base

An implementation of storage_interface that uses standard POSIX functions to perform file I/O.

Public Functions

explicit posix_fd_storage(int fd, std::string reopen_path = null_reopen_path, std::size_t buffer_capacity = default_buffer_capacity)

File descriptor constructor.

  • fd: File descriptor to write to. This will be duplicated via a call to dup(2), so callers may close the file descriptor immediately after this constructor returns if desired.

  • reopen_path: The path of the file associated with the fd argument. This path will be used to reopen the file if requested via the xtrctl reopen command. Pass null_reopen_path if no filename is associated with the file descriptor.

  • buffer_capacity: The size in bytes of the internal write buffer.

Public Static Attributes

static constexpr std::size_t default_buffer_capacity = 64 * 1024

Default value for the buffer_capacity constructor argument.

storage_interface_ptr xtr::make_fd_storage(const char *path)

Creates a storage interface object from a path. If the host kernel supports io_uring(7) and libxtr was built on a machine with liburing header files available then an instance of io_uring_fd_storage will be created, otherwise an instance of posix_fd_storage will be created. To prevent io_uring_fd_storage from being used define set XTR_USE_IO_URING to 0 in xtr/config.hpp.

storage_interface_ptr xtr::make_fd_storage(FILE *fp, std::string reopen_path = null_reopen_path)

Creates a storage interface object from a file descriptor and reopen path. Either an instance of io_uring_fd_storage or posix_fd_storage will be created, refer to make_fd_storage(const char*) for details.

  • fd: File handle to write to. The underlying file descriptor will be duplicated via a call to dup(2), so callers may close the file handle immediately after this function returns if desired.

  • reopen_path: The path of the file associated with the fp argument. This path will be used to reopen the file if requested via the xtrctl reopen command. Pass null_reopen_path if no filename is associated with the file handle.

storage_interface_ptr xtr::make_fd_storage(int fd, std::string reopen_path = null_reopen_path)

Creates a storage interface object from a file descriptor and reopen path. Either an instance of io_uring_fd_storage or posix_fd_storage will be created, refer to make_fd_storage(const char*) for details.

  • fd: File descriptor to write to. This will be duplicated via a call to dup(2), so callers may close the file descriptor immediately after this function returns if desired.

  • reopen_path: The path of the file associated with the fd argument. This path will be used to reopen the file if requested via the xtrctl reopen command. Pass null_reopen_path if no filename is associated with the file descriptor.

constexpr auto xtr::null_reopen_path = ""

When passed to the reopen_path argument of make_fd_storage, posix_fd_storage::posix_fd_storage, io_uring_fd_storage or logger::logger indicates that the output file handle has no associated filename and so should not be reopened if requested by the xtrctl reopen command.

Default Command Path

std::string xtr::default_command_path()

Returns the default command path used for the command_path argument of logger::logger (and other logger constructors). A string with the format “$XDG_RUNTIME_DIR/xtrctl.<pid>.<N>” is returned, where N begins at 0 and increases for each call to the function. If the directory specified by $XDG_RUNTIME_DIR does not exist or is inaccessible then $TMPDIR is used instead. If $XDG_RUNTIME_DIR or $TMPDIR are not set then “/run/user/<uid>” and “/tmp” are used instead, respectively.

Null Command Path

constexpr auto xtr::null_command_path = ""

When passed to the command_path argument of logger::logger (or other logger constructors) indicates that no command socket should be created.

Configuration Variables

The header file xtr/config.hpp contains configuration variables that may be overridden by users.

XTR_SINK_CAPACITY

Sets the capacity (in bytes) of the queue that sinks use to send log data to the background thread. Each sink will have an individual queue of this size. Users are permitted to define this variable in order to set a custom capacity. User provided capacities may be rounded up—to obtain the actual capacity invoke xtr::sink::capacity.

Note that if the single header include file is not used then this setting may only be defined in either config.hpp or by overriding CXXFLAGS, and requires rebuilding libxtr if set.

XTR_USE_IO_URING

Set to 1 to enable io_uring support. If this setting is not manually defined then io_uring support will be automatically detected. If libxtr is built with io_uring support enabled then the library will still function on kernels that do not have io_uring support, as a run-time check will be performed before attempting to use any io_uring system calls.

Note that if the single header include file is not used then this setting may only be defined in either config.hpp or by overriding CXXFLAGS, and requires rebuilding libxtr if set.

XTR_IO_URING_POLL

Set to 1 to enable submission queue polling when using io_uring. If enabled the IORING_SETUP_SQPOLL flag will be passed to io_uring_setup(2).

Note that if the single header include file is not used then this setting may only be defined in either config.hpp or by overriding CXXFLAGS, and requires rebuilding libxtr if set.