As was noted in the tutorial,
the library provides several ways of expressing formatters, most notable
being with a stream-style syntax and Boost.Format-style
expression. Which of the two formats is chosen is determined by the appropriate
anchor expression. To use stream-style syntax one should begin the formatter
definition with a stream
or wstream
keyword, like
that:
#include <boost/log/formatters/stream.hpp> backend->set_formatter(fmt::stream << expr1 << expr2 << ... << exprN);
Here expressions expr1
through
exprN
may be either manipulators,
described in this section, or other expressions resulting in an object that
supports putting into an STL-stream. Note that boost::ref
is
also supported, which allows injecting references into the formatter object.
To use Boost.Format-style
syntax one should use format
construct:
#include <boost/log/formatters/format.hpp> backend->set_formatter(fmt::format("format string") % expr1 % expr2 % ... % exprN);
The format string passed to the format
keyword should contain positional placeholders for the appropriate expressions.
In the case of wide-character logging the format string should be wide. Expressions
expr1
through exprN
have the same meaning as in stream-like
variant. It should be noted though that using stream-like syntax usually
results in a faster formatter than one is constructed with the format
keyword.
Another useful way of expressing formatters is by using string templates. This part of the library is described in this section and is mostly intended to support initialization from the application settings.
#include <boost/log/formatters/attr.hpp>
The attr
formatter is the
most generic and simple way to format an attribute value. In the simplest
form it is equivalent to putting the specified attribute value into an
STL stream with an operator <<
associated with the attribute value
type. For example:
// A custom attribute value type struct A { int n; std::string s; }; // This operator will be called by the formatter std::ostream& operator<< (std::ostream& strm, A const& val) { strm << "[n = " << val.n << ", s = " << val.s << "]"; return strm; } // Sets the formatter to a sink backend backend->set_formatter(fmt::stream << fmt::attr< A >("MyAttribute"));
This will also work with Boost.Format-style syntax since it falls back to streams internally:
// Sets the formatter to a sink backend backend->set_formatter(fmt::format("%1%") % fmt::attr< A >("MyAttribute"));
Like filters, formatters also support specifying a set of possible types of the attribute values in MPL-compatible sequences. For example, you can format an attribute that may have some integral type like this:
typedef boost::mpl::vector< short, int, long >::type types; backend->set_formatter(fmt::stream << fmt::attr< types >("MyInteger"));
The attr
formatter also
supports an additional argument with a Boost.Format
format string. This feature can be used to specify formatting rules for
numbers and/or decorate the formatted attribute somehow. For example:
typedef boost::mpl::vector< short, int, long >::type types; // will print the "MyInteger" attribute value as 8-digit hex in square brackets backend->set_formatter(fmt::stream << fmt::attr< types >("MyInteger", "[%08x]"));
Please note, though, that the format string should be valid for all attribute
value types that are specified as the template argument for the attr
formatter. If this is not possible,
one should consider using conditional
formatters to split types into several groups and specify a valid
format string for each group.
It is possible that at run time the attribute value requested by the formatter is not present in the formatted log record. In this case the formatter by default will throw an exception. If this behavior is not desired, one can change it in one of the following ways:
has_attr
filter to first verify
that the required attribute value is present and apply formatting then.
std::nothrow
argument to the attr
placeholder. In this case the formatter will produce no output for
the missing attribute value, without throwing the exception.
#include <boost/log/formatters/date_time.hpp>
The library provides several formatters dedicated to date and time-related
attribute value types. These are date
,
time
, date_time
,
time_duration
and time_period
. The date
and time
formatters are
mostly included by date_time
.
As comes from its name, date_time
is responsible for formatting time points. The supported attribute value
types are: std::tm
, std::time_t
,
boost::posix_time::ptime
and boost::local_time::local_date_time
from Boost.DateTime.
Additionally, the date
formatter supports the boost::gregorian::date
type. As with other formatters and filters, you can also explicitly specify
the attribute type or set of types that you want to format.
The date and time format can be customized by the user. The format should
be passed as a named argument format
to the appropriate formatter. This argument should be a format string,
containing placeholders described in the Boost.DateTime
documentation. Some usage examples are as follows:
// Puts a date of any of the supported types into log. // The default format is "%Y-%b-%d". backend->set_formatter(fmt::stream << fmt::date("Date")); // Puts a time point of any of the supported types into log. // The default format is "%H:%M:%S.%f". backend->set_formatter(fmt::stream << fmt::time("Time")); // Puts a date and time of the specified type into log. // The default format is "%Y-%b-%d %H:%M:%S.%f". backend->set_formatter(fmt::stream << fmt::date_time< std::tm >("DateTime")); // Puts a date and time of one of the specified types into log. typedef boost::mpl::vector< std::time_t, std::tm, boost::posix_time::ptime >::type types; backend->set_formatter ( fmt::stream << fmt::date_time< types >("DateTime", keywords::format = "%d.%m.%y %H:%M:%S") );
The time_duration
formatter
is responsible for formatting time periods that are not bound to a specific
time point. It supports types double
(interpreted as a result of the difftime
standard function), boost::posix_time::time_duration
and boost::gregorian::date_duration
. The formatter also supports
the format
argument. The
usage is straightforward:
// Puts a time duration of any of the supported types into log. // The default format is "%-%H:%M:%S.%f". backend->set_formatter(fmt::stream << fmt::time_duration("Duration")); // Puts a time duration of the specified type into log. typedef boost::posix_time::time_duration duration_t; backend->set_formatter ( fmt::stream << fmt::time_duration< duration_t >("Duration", keywords::format = "%H:%M:%S") );
The time_period
formatter
composes two time points that denote a time period. It supports the following
types: boost::posix_time::time_period
, boost::local_time::local_time_period
and boost::gregorian::date_period
. The formatter is a bit more
special since it supports two kinds of format strings, the one that describes
the date format, and the one that describes the dates composition. The
former can be specified with the named argument unit_format
and the latter - with named argument format
.
The time period composition format string may contain either of the three
placeholders:
%begin%
- the placeholder will be substituted
with the starting time point of the period.
%last%
- the placeholder will be substituted
with the last time point of the period.
%end%
- the placeholder will be substituted
with the time point that is immediately after the time period.
Here are some usage examples:
// Puts a time period of any of the supported types into log. // The default composition format is [%begin% - %last%]. backend->set_formatter(fmt::stream << fmt::time_period("Period")); // Puts a time period of the specified type into log. typedef boost::posix_time::time_period period_t; backend->set_formatter ( fmt::stream << fmt::time_period< period_t >( "Period", keywords::format = "[%begin%; %end%)", keywords::unit_format = "%d.%m.%y %H:%M:%S") );
Like the attr
formatter,
all date and time formatters throw exceptions when there is no requested
attribute value in the log record. The std::nothrow
argument may be used to suppress exceptions.
#include <boost/log/formatters/named_scope.hpp>
The formatter named_scope
is intended to add support for flexible formatting of the named
scope attribute. The basic usage is quite straightforward and its
result is similar to what attr
provides:
// Puts the scope stack from outer ones towards inner ones: outer scope -> inner scope backend->set_formatter(fmt::stream << fmt::named_scope("Scopes"));
The formatter supports customization with the following named arguments:
iteration
. The argument
describes the direction of iteration through scopes. Can have values
forward
(default) or
reverse
.
delimiter
. The argument
can be used to specify the delimiters between scopes. The default delimiter
depends on the iteration
argument. If iteration == forward
the default delimiter
will be "->", otherwise it will be "<-".
depth
. The argument
can be used to limit the number of scopes to put to log. The formatter
will print depth
innermost
scopes and, if there are more scopes left, append an ellipsis to the
written sequence. By default the formatter will write all scope names.
Here are a few usage examples:
// Puts the scope stack in reverse order: // inner scope <- outer scope backend->set_formatter ( fmt::stream << fmt::named_scope("Scopes", keywords::iteration = fmt::reverse) ); // Puts the scope stack in reverse order with a custom delimiter: // inner scope | outer scope backend->set_formatter ( fmt::stream << fmt::named_scope( "Scopes", keywords::iteration = fmt::reverse, keywords::delimiter = " | ") ); // Puts the scope stack in forward order, no more than 2 inner scopes: // ... -> outer scope -> inner scope backend->set_formatter ( fmt::stream << fmt::named_scope( "Scopes", keywords::iteration = fmt::forward, keywords::depth = 2) ); // Puts the scope stack in reverse order, no more than 2 inner scopes: // inner scope <- outer scope <- ... backend->set_formatter ( fmt::stream << fmt::named_scope( "Scopes", keywords::iteration = fmt::reverse, keywords::depth = 2) );
Like the attr
formatter,
the named scope formatter throws an exception if there is no requested
attribute value in the log record. The std::nothrow
argument may be used to suppress exceptions.
#include <boost/log/formatters/if.hpp>
There are cases when one would want to check some condition about the log record and format it depending on that condition. One example of such a need is formatting an attribute value depending on its runtime type. The general syntax of the conditional formatter is as follows:
if_ (filter) [ true_formatter ] .else_ [ false_formatter ]
Those familiar with Boost.Phoenix
lambda expressions will find this syntax quite familiar. The filter
argument is a filter that is applied
to the record being formatted. If it returns true
,
the true_formatter
is executed,
otherwise false_formatter
is executed. The else_
section with false_formatter
is optional. If it is omitted and filter
yields false
, no formatter
is executed. Here is an example:
backend->set_formatter ( fmt::stream // First, put the current time << fmt::date_time("TimeStamp") << " " << fmt::if_ (flt::has_attr< int >("ID")) [ // if "ID" is integral then format it as a hex number fmt::stream << fmt::attr< int >("ID", "[0x%08x]") ] .else_ [ // otherwise put it as it is fmt::stream << "[" << fmt::attr("ID") << "]" ] // and after that goes the log record text << " " << fmt::message() );
#include <boost/log/formatters/message.hpp>
With all these attributes we shouldn't forget the log message itself, should
we? There is a message
placeholder that represents the text of logging records being formatted.
The usage is quite simple:
// Sets up a formatter that will ignore all attributes and only print log record text backend->set_formatter(fmt::stream << fmt::message());
Please note, that for wide character logging there is a similar wmessage
placeholder.
There are times when one would want to additionally post-process the composed string before passing it to the sink backend. For example, in order to store a log into an XML file the formatted log record should be checked for special characters that have a special meaning in XML documents. This is where decorators step in.
#include <boost/log/formatters/xml_decorator.hpp>
This decorator replaces XML special characters (&, <, > and ')
with the corresponding tokens ('
,
<
, >
and
'
, correspondingly). The usage is as follows:
xml_backend->set_formatter ( // Apply the decoration to the whole formatted record fmt::xml_dec [ fmt::stream << fmt::date_time("TimeStamp") << " " << fmt::message() ] );
Since character decorators are yet another kind of formatters, it's fine to use them in other contexts where formatters are appropriate. For example, this is also a valid example:
xml_backend->set_formatter ( fmt::format("<message>%1%: %2%</message>") % fmt::attr< unsigned int >("LineID") % fmt::xml_dec[ fmt::message() ]; // Only decorate the message text );
There is an example of the library set up for logging into an XML file,
see libs/log/example/xml_file
.
#include <boost/log/formatters/csv_decorator.hpp>
This decorator allows to ensure that the resulting string conforms to the CSV format requirements. In particular, it duplicates the quote characters in the formatted string.
csv_backend->set_formatter ( fmt::stream << fmt::attr< unsigned int >("LineID") << "," << fmt::csv_dec[ fmt::attr< std::string >("Tag") ] << "," << fmt::csv_dec[ fmt::message() ] );
#include <boost/log/formatters/c_decorator.hpp>
The file defines two character decorators: c_dec
and c_ascii_dec
. The first
replaces the following characters with their escaped counerparts: \ (backslash,
0x5c), \a (bell character, 0x07), \b (backspace, 0x08), \f (formfeed, 0x0c),
\n (newline, 0x0a), \r (carriage return, 0x0d), \t (horizontal tabulation,
0x09), \v (vertical tabulation, 0x0b), ' (apostroph, 0x27), " (quote,
0x22), ? (question mark, 0x3f). The c_ascii_dec
decorator does the same but also replaces all other non-printable and non-ASCII
characters with escaped hexadecimal character codes in C notation (e.g.
"\x8c"). The usage is similar to other character decorators:
backend->set_formatter ( fmt::stream << fmt::attr< unsigned int >("LineID") << ": [" << fmt::c_dec[ fmt::attr< std::string >("Tag") ] << "] " << fmt::c_ascii_dec[ fmt::message() ] );