Skip to content
Serge Aleynikov edited this page May 23, 2020 · 23 revisions

The utxx project implements an asynchronous logging framework with pluggable backends.

The implementation uses a lock-free queue where multiple writers can write messages, and a single I/O thread is used to deque messages and propagate them to configured backends.

Currently the following backends are impelemented:

  • disk file
  • syslog
  • console
  • scribe (deprecated)

The framework's implementation is optimized to reduce the log writing latency of a message writer. Since propagation of log messages to backends is happening asynchronously, there's no penalty on a message writer.

The timestamps included in log messages that are passed to back-ends for logging may appear non-contiguous in multi-threaded applications. As the result, the log output may have log lines that seem to be out of order. This is normal, because a timestamp represents the point in time when an event was generated rather than when it was logged, and during the funneling of events through the consuming thread of the logger the multi-threaded message enqueing cannot guarantee the ordering of processed events from different threads. The only guarantee is that messages logged from any given thread are always ordered by timestamp.

Asynchronous message logging backend is pretty efficient, and guarantees that only minimum work is done in the caller's context. All message passing is lock-free.

Logged messages include source code information, optionally including demangled caller's function name.

Crash reports can also be automatically written to the log file.

Using Framework

Add the following include:

#include <utxx/logger.hpp>

In your main() function initialize the logger by providing it a configuration file:

int main(int argc, char* argv[])
{
    ...
    const char* cfg = argv[1];
    logger&     log = logger::instance();
    log.init(argv);  // Initialize the logger

    ...

    log.finalize();  // Finalize the logger's thread and commit all unwritten data to disk
    return 0;
}

In your code use the UTXX_LOG_*(Fmt, ...) macros to write messages to the logger:

UTXX_LOG_DEBUG("This is message#%d\n", 10);
UTXX_LOG_ERROR("This is an error!\n");

If you require to categorize your messages by providing a message category, use "CLOG" macros UTXX_CLOG_*(Fmt, ...):

UTXX_CLOG_DEBUG("encoder",   "This is message#%d\n", 10);
UTXX_CLOG_ERROR("main-loop", "This is an error!\n");

The UTXX_LOG(Level) and UTXX_CLOG(Level) logger macros allow streamed '<<' logging:

UTXX_LOG(ERROR)  << "This is an error #" << 10 << " and bool "
                 << true << ' ' << std::endl;

UTXX_CLOG(ERROR, "main-loop") << "This is an error #" << 10 << " and bool "
                              << true << ' ' << std::endl;

Log Options

The options below may be configured inside the logger node:

logger {
   ...
}

The full configuration schema is defined in the logger_options.xml file. Activation of a particular logger's backend is done through the "inversion of control" design pattern by including a section (e.g. "file", "syslog", "console", etc) in the logger configuration node.

Option Type Default Description
show-location bool false When true the log entries contain File:Line
show-fun-namespaces int 3 Max number of namespaces included in printing function names
  • 0 - no function name is printed.
  • 1 - function name is printed without namespaces.
show-category bool true When true message category is added to output lines
show-ident bool false When true logger's ident property is added to output lines
show-thread str false Add logging thread id (numeric or symbolic) to output. Values: false[='none'],true[='name'],none,id,name. Symbolic name is obtained through call to 'pthread_getname_np()'
ident str Process's identifier (can use env variables in the name)
timestamp str Format of the timestamp (case-insensitive)
no/none/no-timestamp
No timestamp
time
HH:MM:SS
time-msec
HH:MM:SS.ttt
time-usec
HH:MM:SS.tttttt
time-nsec
HH:MM:SS.ttttttttt
date
YYYYmmdd
date-time
YYYYmmdd-HH:MM:SS
date-time-msec
YYYYmmdd-HH:MM:SS.ttt
date-time-usec
YYYYmmdd-HH:MM:SS.tttttt
date-time-nsec
YYYYmmdd-HH:MM:SS.ttttttttt
levels str Mask (delimiter: ' |,;') that specifies minimum severity of messages to log (def: '')
min-level-filter str Minimum severity of messages to log (use ether this one or the 'levels' option) false/none/trace/trace{1..5}/debug/notice/info/warning/error/fatal/alert
wait-timeout-ms int 1000 Wait milliseconds before checking queue of log messages
sched-yield-us int 100 Spin calling sched_yield() for this msec time before sleeping for wait-timeout-ms
silent-finish bool false When true logger doesn't write completion status to log at termination
handle-crash-signals bool true When true logger installs signal handlers (may contain one or more 'signals' nodes)
handle-crash-signals.signals str Pipe/comma-delimitted list of signal handlers (def: SIGABRT,SIGFPE,SIGILL,SIGSEGV,SIGTERM)
block-signals bool true Block all signals by the logger's writing thread

Disk File Backend Options

This backend is used for writing messages to disk file. These options may be configured inside the logger.file node:

logger {
  file {
    ...
  }
}

Upon startup the current log file part is determined by:

  1. If the symlink exists, its unlerlying file is considered to be the current log file name.
  2. Otherwise the name is chosen among all file parts present on disk choosing the one with latest modified timestamp.
Option Type Default Description
filename str Filename of a log file (can use env vars and strftime formatting)
append bool true If true the log file is open in the appending mode, otherwise it's truncated
mode int 0644 Octal file access mask
symlink str Name of the symlink to be created to current log file
levels str Filter of log severity levels to be saved. Delimiter: ' |,;'. Def: info|warning|error|alert|fatal.
show-location bool false Overrides logger.show-location option
show-ident bool false Overrides logger.show-ident option
no-header bool false When enabled, no field definition header is written to file at startup
split-size int 0 If greater than 0 describes the split size in bytes for log file (0 - no splitting)
split-parts int 0 If greater than 0, defines max number of file parts allowed. If not specified, the files keep on getting created indefinitely. Otherwise, the oldest part (determined by file.split-order) gets deleted.
split-order str last Order of split file parts
first
The current log file is always the minimum log file part found on disk at startup up to #1. If no log file is found, the current log file is #1. Older log file parts are renamed such that #2 becomes #3, and so on, and the number equal to "split-parts"+1 gets deleted.
last
Current file is last, having the greatest value in its suffix (i.e. if "split-parts=10", and symlink option is given, the symlynk will always point to the largest file part up to #10, which will be the current log file). Older log file parts are renamed such that #9 becomes #8, and so on, and #1 gets deleted.
rotate
File names are rotated from #1 to "split-parts". The "split-parts" is a required option in this case.
split-delim char '_' Delimiting char used before the part number in a file name (e.g. 'output_5.log')

Syslog Backend Options

This backend is used for writing messages to syslog. The options below may be configured inside the logger.syslog node:

logger {
  syslog {
    ...
  }
}
Option Type Default Description
levels str Pipe or comma-delim filter of severity levels (def: info,warning,error,alert,fatal)
facility str log-local6 Syslog facility to use (log-user, log-local{0-6}, log-daemon)
show-pid bool true When true output includes the pid of current process

Console Backend Options

This backend is used for writing messages to console. The options below may be configured inside the logger.console node:

logger {
  console {
    ...
  }
}
Option Type Default Description
stdout-levels str Filter of log severity levels to print to stdout (def: notice,info,warning)
stderr-levels str Filter of log severity levels to print to stderr (def: error,fatal,alert)
min-stdout-level str notice Minimum severity of messages to log to stdout (this option overrides stdout-levels)
show-location bool false Overrides logger.show-location option
show-ident bool false Overrides logger.show-ident option