Returning to one of the examples in previous tutorial sections:
void init() { logging::init_log_to_file ( keywords::file_name = "sample_%N.log", keywords::rotation_size = 10 * 1024 * 1024, keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), keywords::format = "[%TimeStamp%]: %_%" ); logging::core::get()->set_filter ( flt::attr< logging::trivial::severity_level >("Severity") >= logging::trivial::info ); }
One may wonder how to specify log record formatting rules. In the case of
the init_log_to_file
function,
this is what the format
parameter
for. If you prefer to set up sinks manually, most sink backends provide the
set_formatter
member function
for this purpose.
The format can be specified in a number of ways, described further.
You can create a formatter with a lambda-style expression like this:
void init() { logging::init_log_to_file ( keywords::file_name = "sample_%N.log", // This makes the sink to write log records that look like this: // 1: <normal> A normal severity message // 2: <error> An error severity message keywords::format = ( fmt::stream << fmt::attr< unsigned int >("LineID") << ": <" << fmt::attr< logging::trivial::severity_level >("Severity") << "> " << fmt::message() ) ); }
Here the stream
is a placeholder
for the stream to format the record in. Other insertion arguments, such as
attr
and message
,
are manipulators that define what should be stored in the stream. The message
manipulator is a bit special since,
unlike all other manipulators, it writes a preformatted message acquired
from the logger, not an attribute value. There are other formatter
manipulators that provide advanced support for date, time and other
types.
Some manipulators may accept additional arguments that customize their behavior.
Most of these arguments are named and can be passed in Boost.Parameter
style. For example, attr
supports a format specifier in a printf-style string. For a change, let's
see how it's done when manually initializing sinks:
void init() { typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink; boost::shared_ptr< text_sink > pSink = boost::make_shared< text_sink >(); pSink->locked_backend()->add_stream( boost::make_shared< std::ofstream >("sample.log")); pSink->locked_backend()->set_formatter ( fmt::stream // line id will be written in hex, 8-digits, zero-filled << fmt::attr< unsigned int >("LineID", keywords::format = "%08x") << ": <" << fmt::attr< logging::trivial::severity_level >("Severity") << "> " << fmt::message() ); logging::core::get()->add_sink(pSink); }
For the reference of the supported arguments see the reference of the corresponding manipulator. More manipulators are described in the Detailed features description section.
As an alternative, you can define formatters with with a syntax similar to Boost.Format. The same formatter as described above can be written as follows:
void init() { typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink; boost::shared_ptr< text_sink > pSink = boost::make_shared< text_sink >(); pSink->locked_backend()->add_stream( boost::make_shared< std::ofstream >("sample.log")); // This makes the sink to write log records that look like this: // 00000001: <normal> A normal severity message // 00000002: <error> An error severity message pSink->locked_backend()->set_formatter ( fmt::format("%1%: <%2%> %3%") % fmt::attr< unsigned int >("LineID", keywords::format = "%08x") % fmt::attr< logging::trivial::severity_level >("Severity") % fmt::message() ); logging::core::get()->add_sink(pSink); }
The format
placeholder accepts
the format string with positional specification of all arguments being formatted.
Note that only positional format is currently supported. The same format
specification can be used with the init_log_to_file
and similar functions.
In some contexts textual templates are accepted as formatters. In this case library initialization support code is invoked in order to parse the template and reconstruct the appropriate formatter. There are a number of caveats to keep in mind when using this approach, but here it will suffice to just briefly describe the template format.
void init() { logging::init_log_to_file ( keywords::file_name = "sample_%N.log", keywords::format = "[%TimeStamp%]: %_%" ); logging::core::get()->set_filter ( flt::attr< logging::trivial::severity_level >("Severity") >= logging::trivial::info ); }
Here, the format
parameter
accepts such a format template. The template may contain a number of placeholders
enclosed with percent signs (%
).
Each placeholder must contain an attribute value name to insert instead of
the placeholder. The %_%
placeholder is special as it will be replaced
with the logging record message.
Note | |
---|---|
Textual format templates are not accepted by sink backends in the |
You can add a custom formatter to a sink backend that supports formatting. The formatter is actually a function object that supports the following signature:
void (std::basic_ostream< CharT >& strm, logging::basic_record< CharT > const& rec);
Here CharT
is the character
type that is used with the library. The formatter will be invoked whenever
a log record rec
passes filtering
and is to be stored in log. The formatted record should be composed by insertion
into STL-compatible output stream strm
.
Here's an example of a custom formatter function usage:
void my_formatter(std::ostream& strm, logging::record const& rec) { namespace lambda = boost::lambda; unsigned int line_id; if (logging::extract< unsigned int >( "LineID", rec.attribute_values(), lambda::var(line_id) = lambda::_1)) { strm << line_id << ": "; } logging::trivial::severity_level severity; if (logging::extract< logging::trivial::severity_level >( "Severity", rec.attribute_values(), lambda::var(severity) = lambda::_1)) { strm << "<" << severity << "> "; } strm << rec.message(); } void init() { typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink; boost::shared_ptr< text_sink > pSink = boost::make_shared< text_sink >(); pSink->locked_backend()->add_stream( boost::make_shared< std::ofstream >("sample.log")); pSink->locked_backend()->set_formatter(&my_formatter); logging::core::get()->add_sink(pSink); }