The log
library is a small (but not single-header) library for helping to
manage debug, informational and error output.
Using default log facilities.
log::level("debug", 2);
DEBUG(1) << "Debug message, level 1.";
LOG(0) << "Log to default logger, level 0.";
Create facilities on the fly.
LOG("solver",1) << "This goes to the 'solver' facility."
Customize handling of log records.
log::default_sink(my_log_handler); // set default handler
log::sink("solver", my_other_log_handler); // set handler for 'solver'
Use macro-forms to avoid evaluation of message.
LOG("solver",3) << expensive_message();
Use function forms to ensure evaluation of message. Note that source location info, if required, has to be supplied 'by hand'.
log::log("solver",3) << LOG_LOC << i_have_side_effects();
Facilities can be referenced by name or by object.
LOG("solver") << "A message.";
auto solver = log::facility("solver");
LOG(solver) << "A second message (with source location).";
solver << "A third message, omitting source location.";
Some simple log handlers are included
log::sink(log::file_sink("run.log", log::flag::flush, log::flag::noemitloc));
and these can be extended by sub-classing.
struct my_stream_sink: public log::stream_sink {
template <typename... Args>
my_sink(Args&&... args): log::stream_sink(std::forward<Args>(args)...) {}
protected:
void format_location(std::ostream& out, log::source_location loc) override {
out << "FILE: " << loc.file << "; ";
}
};
log::sink(my_stream_sink(std::cerr));
Goals:
- multiple logging facilities;
- each facility can have an independent handler or log level;
- facilities can be referenced by name or by object;
- one can log with forced or conditional evaluation of the message;
- facility creation and parameter setting is thread safe.
A facility is described by a tuple: name, level, sink. Responsibility for
facilities lies with a facility_manager
; there is a global facility_manager
that is used by default.
When a facility is constructed from a const char*
name, an existing facility
is retrieved from the manager, or a new one constructed if none yet exist with
that name. Newly created facilities adopt the manager's current default sink
and log level.
Facilities are used for logging with operator()
(taking a message level) or
directly as the left-hand operand of operator<<
. These both create a
temporary sink_stream
object, derived from std::ostream
, that sends the
composed message to the facility's sink on destruction. The end of a log entry
is implictly determined by the end of the logging statement:
logger << "This all comprises exactly " << 1 << " record.";
Source location information is provided by writing a source_location
object
to the sink_stream
. A source_location
corresponding to the current source
line is created by the macro LOG_LOC
; this is added automatically when one of
the logging macros (see below) is used to write an entry to the logging
facility.
logger << source_location{"file.cc", 200, "foo()"} << "with explicit line info.";
logger << LOG_LOC << "with line info for this source line.";
LOG(logger) << "line info included automatically.";
A log entry produced by a facility is represented by a log_entry
structure
with fields for the facility name, the message level, the source location, and
the message text.
Sinks are represented by a std::function<void (const log::log_entry&)>
object; the objects refered to by fields in the log_entry
are not guaranteed
to have a lifetime longer than that of the log_entry
object itself.
The LOG
macro dispatches on the number of arguments (one or two). LOG(n)
is
equivalent to LOG(::log::log, n)
(where log::log
is the default logging
facility).
LOG(fac, n)
expands to
if (auto log_magic_reserved_temp_ = ::log::log_test_proxy(::log::facility(fac)(n))) ;
else log_magic_reserved_temp_.stream << LOG_LOC
Here log::log_test_proxy
returns a wrapper which converts to false if and
only if the wrapped stream is in a good state.
Two other macros correspond to the predefined streams debug
and
assertion_failure
. DEBUG(n)
is equivalent to LOG(::log::debug, n)
, unless
LOG_NDEBUG
is defined, in which case it expands to a no-op.
ASSERT(test)
is equivalent to
if (test) ; else ::log::assertion_failure << LOG_LOC
unless LOG_NASSERT
is defined, in which case it expands to a no-op.
A customizable sink object log::stream_sink
will write log records to a
supplied stream, with behaviour governed by flags controlling whether to print
source locations, or flush the stream after each record.
log::stream_sink
uses log::locked_ostream
to coordinate access to streams
shared across multiple sinks and to maintain independent formatting state.