-
Notifications
You must be signed in to change notification settings - Fork 14
Logging Framework
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.
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;
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
|
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)
|
|
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 |
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:
- If the symlink exists, its unlerlying file is considered to be the current log file name.
- 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
|
split-delim | char | '_' | Delimiting char used before the part number in a file name (e.g. 'output_5.log') |
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 |
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 |