Boost C++ Libraries

PrevUpHomeNext

Formatters

Generic attribute formatter
Date and time formatter
Named scope formatter
Conditional formatters
Message placeholder
Character decorators

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:

  1. Use the conditional formatter with the has_attr filter to first verify that the required attribute value is present and apply formatting then.
  2. Use an additional last 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.

XML character decorator
#include <boost/log/formatters/xml_decorator.hpp>

This decorator replaces XML special characters (&, <, > and ') with the corresponding tokens (&apos;, &lt;, &gt; and &apos;, 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.

CSV character decorator
#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() ]
);
C-style character decorators
#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() ]
);

PrevUpHomeNext