### Lambda expressions

As it was pointed out in tutorial, filters and formatters can be specified as Lambda expressions with placeholders for attribute values. This section will describe the placeholders that can be used to build more complex Lambda expressions.

There is also a way to specify the filter in the form of a string template. This can be useful for initialization from the application settings. This part of the library is described here.

#### Generic attribute placeholder

    #include <boost/log/expressions/attr_fwd.hpp>
    #include <boost/log/expressions/attr.hpp>

The `attr` placeholder represents an attribute value in template expressions. Given the record view or a set of attribute values, the placeholder will attempt to extract the specified attribute value from the argument upon invocation. This can be roughly described with the following pseudo-code:

    logging::value_ref< T, TagT > val = expr::attr< T, TagT >(name)(rec);

where `val` is the reference to the extracted value, `name` and `T` are the attribute value name and type, `TagT` is an optional tag (we'll return to it in a moment) and `rec` is the log record view or attribute value set. `T` can be a Boost.MPL type sequence with possible expected types of the value; the extraction will succeed if the type of the value matches one of the types in the sequence.

The `attr` placeholder can be used in Boost.Phoenix expressions, including the `bind` expression.

    bool my_filter(logging::value_ref< severity_level, tag::severity > const& level,
                   logging::value_ref< std::string, tag::tag_attr > const& tag)
    {
        return level >= warning || tag == "IMPORTANT_MESSAGE";
    }

    void init()
    {
        // ...

        namespace phoenix = boost::phoenix;
        sink->set_filter(phoenix::bind(&my_filter, severity.or_none(), tag_attr.or_none()));

        // ...
    }

The placeholder can be used both in filters and formatters:

    sink->set_filter
    (
        expr::attr< int >("Severity") >= 5 &&
        expr::attr< std::string >("Channel") == "net"
    );

    sink->set_formatter
    (
        expr::stream
            << expr::attr< int >("Severity")
            << " [" << expr::attr< std::string >("Channel") << "] "
            << expr::smessage
    );

The call to `set_filter` registers a composite filter that consists of two elementary subfilters: the first one checks the severity level, and the second checks the channel name. The call to `set_formatter` installs a formatter that composes a string containing the severity level and the channel name along with the message text.

##### Customizing fallback policy

By default, when the requested attribute value is not found in the record, `attr` will return an empty reference. In case of filters, this will result in `false` in any ordering expressions, and in case of formatters the output from the placeholder will be empty. This behavior can be changed:

- To throw an exception (`missing_value` or `invalid_type`, depending on the reason of the failure). Add the `or_throw` modifier:

    sink->set_filter
    (
        expr::attr< int >("Severity").or_throw() >= 5 &&
        expr::attr< std::string >("Channel").or_throw() == "net"
    );

- To use a default value instead. Add the `or_default` modifier with the desired default value:

    sink->set_filter
    (
        expr::attr< int >("Severity").or_default(0) >= 5 &&
        expr::attr< std::string >("Channel").or_default(std::string("general")) == "net"
    );

| Tip
You can also use the `has_attr` predicate to implement filters and formatters conditional on the attribute value presence.

The default behavior is also accessible through the `or_none` modifier. The modified placeholders can be used in filters and formatters just the same way as the unmodified ones.

In `bind` expressions, the bound function object will still receive the `value_ref`-wrapped values in place of the modified `attr` placeholder. Even though both `or_throw` and `or_default` modifiers guarantee that the bound function will receive a filled reference, `value_ref` is still needed if the value type is specified as a type sequence. Also, the reference wrapper may contain a tag type which may be useful for formatting customization.

##### Attribute tags and custom formatting operators

The `TagT` type in the abstract description of `attr` above is optional and by default is `void`. This is an attribute tag which can be used to customize the output formatters produce for different attributes. This tag is forwarded to the `to_log` manipulator when the extracted attribute value is put to a stream (this behavior is warranted by `value_ref` implementation). Here's a quick example:

    // We define our own severity levels
    enum severity_level
    {
        normal,
        notification,
        warning,
        error,
        critical
    };

    // The operator is used for regular stream formatting
    std::ostream& operator<< (std::ostream& strm, severity_level level)
    {
        static const char* strings[] =
        {
            "normal",
            "notification",
            "warning",
            "error",
            "critical"
        };

        if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
            strm << strings[level];
        else
            strm << static_cast< int >(level);

        return strm;
    }

    // Attribute value tag type
    struct severity_tag;

    // The operator is used when putting the severity level to log
    logging::formatting_ostream& operator<<
    (
        logging::formatting_ostream& strm,
        logging::to_log_manip< severity_level, severity_tag > const& manip
    )
    {
        static const char* strings[] =
        {
            "NORM",
            "NTFY",
            "WARN",
            "ERRR",
            "CRIT"
        };

        severity_level level = manip.get();
        if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
            strm << strings[level];
        else
            strm << static_cast< int >(level);

        return strm;
    }

    void init()
    {
        logging::add_console_log
        (
            std::clog,
            // This makes the sink to write log records that look like this:
            // 1: <NORM> A normal severity message
            // 2: <ERRR> An error severity message
            keywords::format =
            (
                expr::stream
                    << expr::attr< unsigned int >("LineID")
                    << ": <" << expr::attr< severity_level, severity_tag >("Severity")
                    << "> " << expr::smessage
            )
        );
    }

See the complete code.

Here we specify a different formatting operator for the severity level wrapped in the `to_log_manip` manipulator marked with the tag `severity_tag`. This operator will be called when log records are formatted while the regular `operator<<` will be used in other contexts.

#### Defining attribute keywords

    #include <boost/log/expressions/keyword_fwd.hpp>
    #include <boost/log/expressions/keyword.hpp>

Attribute keywords can be used as replacements for the `attr` placeholders in filters and formatters while providing a more concise and less error prone syntax. An attribute keyword can be declared with the `BOOST_LOG_ATTRIBUTE_KEYWORD` macro:

    BOOST_LOG_ATTRIBUTE_KEYWORD(keyword, "Keyword", type)

Here the macro declares a keyword `keyword` for an attribute named "Keyword" with the value type of `type`. Additionally, the macro defines an attribute tag type `keyword` within the `tag` namespace. We can rewrite the previous example in the following way:

    // We define our own severity levels
    enum severity_level
    {
        normal,
        notification,
        warning,
        error,
        critical
    };

    // Define the attribute keywords
    BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)

    // The operator is used for regular stream formatting
    std::ostream& operator<< (std::ostream& strm, severity_level level)
    {
        static const char* strings[] =
        {
            "normal",
            "notification",
            "warning",
            "error",
            "critical"
        };

        if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
            strm << strings[level];
        else
            strm << static_cast< int >(level);

        return strm;
    }

    // The operator is used when putting the severity level to log
    logging::formatting_ostream& operator<<
    (
        logging::formatting_ostream& strm,
        logging::to_log_manip< severity_level, tag::severity > const& manip
    )
    {
        static const char* strings[] =
        {
            "NORM",
            "NTFY",
            "WARN",
            "ERRR",
            "CRIT"
        };

        severity_level level = manip.get();
        if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
            strm << strings[level];
        else
            strm << static_cast< int >(level);

        return strm;
    }

    void init()
    {
        logging::add_console_log
        (
            std::clog,
            // This makes the sink to write log records that look like this:
            // 1: <NORM> A normal severity message
            // 2: <ERRR> An error severity message
            keywords::format =
            (
                expr::stream
                    << line_id
                    << ": <" << severity
                    << "> " << expr::smessage
            )
        );
    }

Attribute keywords behave the same way as the `attr` placeholders and can be used both in filters and formatters. The `or_throw` and `or_default` modifiers are also supported.

Keywords can also be used in attribute value lookup expressions in log records and attribute value sets:

    void print_severity(logging::record_view const& rec)
    {
        logging::value_ref< severity_level, tag::severity > level = rec[severity];
        std::cout << level << std::endl;
    }

#### Record placeholder

    #include <boost/log/expressions/record.hpp>

The `record` placeholder can be used in `bind` expressions to pass the whole log record view to the bound function object.

    void my_formatter(logging::formatting_ostream& strm, logging::record_view const& rec)
    {
        // ...
    }

    namespace phoenix = boost::phoenix;
    sink->set_formatter(phoenix::bind(&my_formatter, expr::stream, expr::record));

| Note
In case of filters, the placeholder will correspond to the set of attribute values rather than the log record itself. This is because the record is not constructed yet at the point of filtering, and filters only operate on the set of attribute values.

#### Message text placeholders

    #include <boost/log/expressions/message.hpp>

Log records typically contain a special attribute "Message" with the value of one of the string types (more specifically, an `std::basic_string` specialization). This attribute contains the text of the log message that is constructed at the point of the record creation. This attribute is only constructed after filtering, so filters cannot use it. There are several keywords to access this attribute value:

- `smessage` - the attribute value is expected to be an `std::string`
- `wmessage` - the attribute value is expected to be an `std::wstring`
- `message` - the attribute value is expected to be an `std::string` or `std::wstring`

The `message` keyword has to dispatch between different string types, so it is slightly less efficient than the other two keywords. If the application is able to guarantee the fixed character type of log messages, it is advised to use the corresponding keyword for better performance.

    // Sets up a formatter that will ignore all attributes and only print log record text
    sink->set_formatter(expr::stream << expr::message);

#### Predicate expressions

This section describes several expressions that can be used as predicates in the filtering expressions.

##### Attribute presence filter

    #include <boost/log/expressions/predicates/has_attr.hpp>

The filter `has_attr` checks if an attribute value with the specified name and, optionally, type is attached to a log record. If no type specified to the filter, the filter returns `true` if any value with the specified name is found. If an MPL-compatible type sequence in specified as a value type, the filter returns `true` if a value with the specified name and one of the specified types is found.

This filter is usually used in conjunction with conditional formatters, but it also can be used as a quick filter based on the log record structure. For example, one can use this filter to extract statistic records and route them to a specific sink.

    // Declare attribute keywords
    BOOST_LOG_ATTRIBUTE_KEYWORD(stat_stream, "StatisticStream", std::string)
    BOOST_LOG_ATTRIBUTE_KEYWORD(change, "Change", int)

    // A simple sink backend to accumulate statistic information
    class my_stat_accumulator :
        public sinks::basic_sink_backend< sinks::synchronized_feeding >
    {
        // A map of accumulated statistic values,
        // ordered by the statistic information stream name
        typedef std::map< std::string, int > stat_info_map;
        stat_info_map m_stat_info;

    public:
        // Destructor
        ~my_stat_accumulator()
        {
            // Display the accumulated data
            stat_info_map::const_iterator it = m_stat_info.begin(), end = m_stat_info.end();
            for (; it != end; ++it)
            {
                std::cout << "Statistic stream: " << it->first
                    << ", accumulated value: " << it->second << "\n";
            }
            std::cout.flush();
        }

        // The method is called for every log record being put into the sink backend
        void consume(logging::record_view const& rec)
        {
            // First, acquire statistic information stream name
            logging::value_ref< std::string, tag::stat_stream > name = rec[stat_stream];
            if (name)
            {
                // Next, get the statistic value change
                logging::value_ref< int, tag::change > change_amount = rec[change];
                if (change_amount)
                {
                    // Accumulate the statistic data
                    m_stat_info[name.get()] += change_amount.get();
                }
            }
        }
    };

    // The function registers two sinks - one for statistic information,
    // and another one for other records
    void init()
    {
        boost::shared_ptr< logging::core > core = logging::core::get();

        // Create a backend and attach a stream to it
        boost::shared_ptr< sinks::text_ostream_backend > backend =
            boost::make_shared< sinks::text_ostream_backend >();
        backend->add_stream(
            boost::shared_ptr< std::ostream >(new std::ofstream("test.log")));

        // Create a frontend and setup filtering
        typedef sinks::synchronous_sink< sinks::text_ostream_backend > log_sink_type;
        boost::shared_ptr< log_sink_type > log_sink(new log_sink_type(backend));
        // All records that don't have a "StatisticStream" attribute attached
        // will go to the "test.log" file
        log_sink->set_filter(!expr::has_attr(stat_stream));

        core->add_sink(log_sink);

        // Create another sink that will receive all statistic data
        typedef sinks::synchronous_sink< my_stat_accumulator > stat_sink_type;
        boost::shared_ptr< stat_sink_type > stat_sink(new stat_sink_type());
        // All records with a "StatisticStream" string attribute attached
        // will go to the my_stat_accumulator sink
        stat_sink->set_filter(expr::has_attr(stat_stream));

        core->add_sink(stat_sink);
    }

    // This simple macro will simplify putting statistic data into a logger
    #define PUT_STAT(lg, stat_stream_name, change)\
        if (true) {\
            BOOST_LOG_SCOPED_LOGGER_TAG(lg, "StatisticStream", stat_stream_name);\
            BOOST_LOG(lg) << logging::add_value("Change", (int)(change));\
        } else ((void)0)

    void logging_function()
    {
        src::logger lg;

        // Put a regular log record, it will go to the "test.log" file
        BOOST_LOG(lg) << "A regular log record";

        // Put some statistic data
        PUT_STAT(lg, "StreamOne", 10);
        PUT_STAT(lg, "StreamTwo", 20);
        PUT_STAT(lg, "StreamOne", -5);
    }

See the complete code.

In this example, log records emitted with the `PUT_STAT` macro will be directed to the `my_stat_accumulator` sink backend, which will accumulate the changes passed in the "Change" attribute values. All other records (even those made through the same logger) will be passed to the filter sink. This is achieved with the mutually exclusive filters set for the two sinks.

Please note that in the example above we extended the library in two ways: we defined a new sink backend `my_stat_accumulator` and a new macro `PUT_STAT`. Also note that `has_attr` can accept attribute keywords to identify the attribute to check.

##### Range checking filter

    #include <boost/log/expressions/predicates/is_in_range.hpp>

The `is_in_range` predicate checks that the attribute value fits in the half-open range (i.e. it returns `true` if the attribute value `x` satisfies the following condition: `left <= x < right`). For example:

    sink->set_filter
    (
        // drops all records that have level below 3 or greater than 4
        expr::is_in_range(expr::attr< int >("Severity"), 3, 5)
    );

The attribute can also be identified by an attribute keyword or name and type:

    sink->set_filter
    (
        expr::is_in_range(severity, 3, 5)
    );

    sink->set_filter
    (
        expr::is_in_range< int >("Severity", 3, 5)
    );

##### Simple string matching filters

    #include <boost/log/expressions/predicates/begins_with.hpp>
    #include <boost/log/expressions/predicates/ends_with.hpp>
    #include <boost/log/expressions/predicates/contains.hpp>

Predicates `begins_with`, `ends_with` and `contains` provide an easy way of matching string attribute values. As follows from their names, the functions construct filters that return `true` if an attribute value begins with, ends with or contains the specified substring, respectively. The string comparison is case sensitive.

    sink->set_filter
    (
        // selects only records that are related to Russian web domains
        expr::ends_with(expr::attr< std::string >("Domain"), ".ru")
    );

The attribute can also be identified by an attribute keyword or name and type.

##### Advanced string matching filter

    #include <boost/log/expressions/predicates/matches.hpp>

    // Supporting headers
    #include <boost/log/support/regex.hpp>
    #include <boost/log/support/xpressive.hpp>
    #include <boost/log/support/spirit_qi.hpp>
    #include <boost/log/support/spirit_classic.hpp>

The `matches` function creates a filter that apples a regular expression or a parser to a string attribute value. The regular expression can be provided by Boost.Regex or Boost.Xpressive. Parsers from Boost.Spirit and Boost.Spirit2 are also supported. The filter returns `true` if the regular expression matches or the parser successfully parses the attribute value.

| Note
In order to use this predicate, a corresponding supporting header should also be included.

    sink->set_filter
    (
        expr::matches(expr::attr< std::string >("Domain"), boost::regex("www\\..*\\.ru"))
    );

The attribute can also be identified by an attribute keyword or name and type.

##### Severity threshold per channel filter

    #include <boost/log/expressions/predicates/channel_severity_filter.hpp>

This filter is aimed for a specific but commonly encountered use case. The `channel_severity_filter` function creates a predicate that will check log record severity levels against a threshold. The predicate allows setting different thresholds for different channels. The mapping between channel names and severity thresholds can be filled in `std::map` style by using the subscript operator or by calling `add` method on the filter itself (the `channel_severity_filter_actor` instance). Let's see an example:

    // We define our own severity levels
    enum severity_level
    {
        normal,
        notification,
        warning,
        error,
        critical
    };

    // Define the attribute keywords
    BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
    BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)

    void init()
    {
        // Create a minimal severity table filter
        typedef expr::channel_severity_filter_actor< std::string, severity_level > min_severity_filter;
        min_severity_filter min_severity = expr::channel_severity_filter(channel, severity);

        // Set up the minimum severity levels for different channels
        min_severity["general"] = notification;
        min_severity["network"] = warning;
        min_severity["gui"] = error;

        logging::add_console_log
        (
            std::clog,
            keywords::filter = min_severity || severity >= critical,
            keywords::format =
            (
                expr::stream
                    << line_id
                    << ": <" << severity
                    << "> [" << channel << "] "
                    << expr::smessage
            )
        );
    }

    // Define our logger type
    typedef src::severity_channel_logger< severity_level, std::string > logger_type;

    void test_logging(logger_type& lg, std::string const& channel_name)
    {
        BOOST_LOG_CHANNEL_SEV(lg, channel_name, normal) << "A normal severity level message";
        BOOST_LOG_CHANNEL_SEV(lg, channel_name, notification) << "A notification severity level message";
        BOOST_LOG_CHANNEL_SEV(lg, channel_name, warning) << "A warning severity level message";
        BOOST_LOG_CHANNEL_SEV(lg, channel_name, error) << "An error severity level message";
        BOOST_LOG_CHANNEL_SEV(lg, channel_name, critical) << "A critical severity level message";
    }

See the complete code.

The filter for the console sink is composed from the `channel_severity_filter_actor` filter and a general severity level check. This general check will be used when log records do not have a channel attribute or the channel name is not one of those specified in `channel_severity_filter_actor` initialization. It should be noted that it is possible to set the default result of the threshold filter that will be used in this case; the default result can be set by the `set_default` method. The `channel_severity_filter_actor` filter is set up to limit record severity levels for channels "general", "network" and "gui" - all records in these channels with levels below the specified thresholds will not pass the filter and will be ignored.

The threshold filter is implemented as an equivalent to `std::map` over the channels, which means that the channel value type must support partial ordering. Obviously, the severity level type must also support ordering to be able to be compared against thresholds. By default the predicate will use `std::less` equivalent for channel name ordering and `std::greater_equal` equivalent to compare severity levels. It is possible to customize the ordering predicates. Consult the reference of the `channel_severity_filter_actor` class and `channel_severity_filter` generator to see the relevant template parameters.

##### Debugger presence filter

    #include <boost/log/expressions/predicates/is_debugger_present.hpp>

This filter is implemented for Windows only. The `is_debugger_present` filter returns `true` if the application is run under a debugger and `false` otherwise. It does not use any attribute values from the log record. This predicate is typically used with the debugger output sink.

    // Complete sink type
    typedef sinks::synchronous_sink< sinks::debug_output_backend > sink_t;

    void init_logging()
    {
        boost::shared_ptr< logging::core > core = logging::core::get();

        // Create the sink. The backend requires synchronization in the frontend.
        boost::shared_ptr< sink_t > sink(new sink_t());

        // Set the special filter to the frontend
        // in order to skip the sink when no debugger is available
        sink->set_filter(expr::is_debugger_present());

        core->add_sink(sink);
    }

See the complete code.

#### Formatting expressions

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 the `stream` keyword, like that:

    #include <boost/log/expressions/formatters/stream.hpp>

    sink->set_formatter(expr::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.

To use Boost.Format-style syntax one should use `format` construct:

    #include <boost/log/expressions/formatters/format.hpp>

    sink->set_formatter(expr::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 the one 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.

##### Date and time formatter

    #include <boost/log/expressions/formatters/date_time.hpp>

    // Supporting headers
    #include <boost/log/support/date_time.hpp>

The library provides the `format_date_time` formatter dedicated to date and time-related attribute value types. The function accepts the attribute value name and the format string compatible with Boost.DateTime.

    sink->set_formatter
    (
        expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
    );

The attribute value can alternatively be identified with the `attr` placeholder or the attribute keyword.

The following placeholders are supported in the format string:

Table 1.2. Date format placeholders

Placeholder | Meaning | Example
--- | --- | ---
%a | Abbreviated weekday name | "Mon" => Monday
%A | Long weekday name | "Monday"
%b | Abbreviated month name | "Feb" => February
%B | Long month name | "February"
%d | Numeric day of month with leading zero | "01"
%e | Numeric day of month with leading space | " 1"
%m | Numeric month, 01-12 | "01"
%w | Numeric day of week, 1-7 | "1"
%y | Short year | "12" => 2012
%Y | Long year | "2012"

Table 1.3. Time format placeholders

Placeholder | Meaning | Example
--- | --- | ---
%f | Fractional seconds with leading zeros | "000231"
%H, %O | Hours in 24 hour clock or hours in time duration types with leading zero if less than 10 | "07"
%I | Hours in 12 hour clock with leading zero if less than 10 | "07"
%k | Hours in 24 hour clock or hours in time duration types with leading space if less than 10 | " 7"
%l | Hours in 12 hour clock with leading space if less than 10 | " 7"
%M | Minutes | "32"
%p | AM/PM mark, uppercase | "AM"
%P | AM/PM mark, lowercase | "am"
%q | ISO time zone | "-0700" => Mountain Standard Time
%Q | Extended ISO time zone | "-05:00" => Eastern Standard Time
%S | Seconds | "26"

Table 1.4. Miscellaneous placeholders

Placeholder | Meaning | Example
--- | --- | ---
%- | Negative sign in case of time duration, if the duration is less than zero | "-"
%+ | Sign of time duration, even if positive | "+"
%% | An escaped percent sign | "%"
%T | Extended ISO time, equivalent to "%H:%M:%S" | "07:32:26"

Note that in order to use this formatter you will also have to include a supporting header. When `boost/log/support/date_time.hpp` is included, the formatter supports the following types of Boost.DateTime:

- Date and time types: `boost::posix_time::ptime` and `boost::local_time::local_date_time`.
- Gregorian date type: `boost::gregorian::date`.
- Time duration types: `boost::posix_time::time_duration` as well as all the specialized time units such as `boost::posix_time::seconds`, including subsecond units.
- Date duration types: `boost::gregorian::date_duration`.

| Tip
Boost.DateTime already provides formatting functionality implemented as a number of locale facets. This functionality can be used instead of this formatter, although the formatter is expected to provide better performance.

##### Named scope formatter

    #include <boost/log/expressions/formatters/named_scope.hpp>

The formatter `format_named_scope` is intended to add support for flexible formatting of the named scope attribute values. 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
    sink->set_formatter(expr::stream << expr::format_named_scope("Scopes", "%n"));

The first argument names the attribute and the second is the format string. The string can contain the following placeholders:

Table 1.5. Named scope format placeholders

Placeholder | Meaning | Example
--- | --- | ---
%n | Scope name | "void foo()"
%f | Source file name of the scope | "foo.cpp"
%l | Line number in the source file | "45"

While the format string describes the presentation of each named scope in the list, the following named arguments allow to customize the list traversal and formatting:

- `format`. The named scope format string, as described above. This parameter is used to specify the format when other named parameters are used.
- `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 (file:line) <- outer scope (file:line)
    sink->set_formatter
    (
        expr::stream
            << expr::format_named_scope(
                "Scopes",
                keywords::format = "%n (%f:%l)",
                keywords::iteration = expr::reverse)
    );

    // Puts the scope stack in reverse order with a custom delimiter:
    // inner scope | outer scope
    sink->set_formatter
    (
        expr::stream
            << expr::format_named_scope(
                "Scopes",
                keywords::format = "%n",
                keywords::iteration = expr::reverse,
                keywords::delimiter = " | ")
    );

    // Puts the scope stack in forward order, no more than 2 inner scopes:
    // ... -> outer scope -> inner scope
    sink->set_formatter
    (
        expr::stream
            << expr::format_named_scope(
                "Scopes",
                keywords::format = "%n",
                keywords::iteration = expr::forward,
                keywords::depth = 2)
    );

    // Puts the scope stack in reverse order, no more than 2 inner scopes:
    // inner scope <- outer scope <- ...
    sink->set_formatter
    (
        expr::stream
            << expr::format_named_scope(
                "Scopes",
                keywords::format = "%n",
                keywords::iteration = expr::reverse,
                keywords::depth = 2)
    );

##### Conditional formatters

    #include <boost/log/expressions/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:

    expr::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:

    sink->set_formatter
    (
        expr::stream
            // First, put the current time
            << expr::format_date_time("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") << " "
            << expr::if_ (expr::has_attr< int >("ID"))
               [
                   // if "ID" is present then put it to the record
                   expr::stream << expr::attr< int >("ID")
               ]
               .else_
               [
                   // otherwise put a missing marker
                   expr::stream << "--"
               ]
            // and after that goes the log record text
            << " " << expr::message
    );

##### Character decorators

There are times when one would like to additionally post-process the composed string before passing it to the sink backend. For example, in order to store 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.

| Note
Unlike most other formatters, decorators are dependent on the character type of the formatted output and this type cannot be deduced from the decorated formatter. By default, the character type is assumed to be `char`. If the formatter is used to compose a wide-character string, prepend the decorator name with the `w` letter (e.g. use `wxml_decor` instead of `xml_decor`). Also, for each decorator there is a generator function that accepts the character type as a template parameter; the function is named similarly to the decorator prepended with the `make_` prefix (e.g. `make_xml_decor`).

###### XML character decorator

    #include <boost/log/expressions/formatters/xml_decorator.hpp>

This decorator replaces XML special characters (&, <, > and ') with the corresponding tokens (`&amp;`, `&lt;`, `&gt;` and `&apos;`, correspondingly). The usage is as follows:

    xml_sink->set_formatter
    (
        // Apply the decoration to the whole formatted record
        expr::xml_decor
        [
            expr::stream << expr::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_sink->set_formatter
    (
        expr::format("<message>%1%: %2%</message>")
            % expr::attr< unsigned int >("LineID")
            % expr::xml_decor[ expr::stream << expr::message ]; // Only decorate the message text
    );

There is an example of the library set up for logging into an XML file, see here.

###### CSV character decorator

    #include <boost/log/expressions/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_sink->set_formatter
    (
        expr::stream
            << expr::attr< unsigned int >("LineID") << ","
            << expr::csv_decor[ expr::stream << expr::attr< std::string >("Tag") ] << ","
            << expr::csv_decor[ expr::stream << expr::message ]
    );

###### C-style character decorators

    #include <boost/log/expressions/formatters/c_decorator.hpp>

The header defines two character decorators: `c_decor` and `c_ascii_decor`. The first one replaces the following characters with their escaped counterparts: \ (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_decor` 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:

    sink->set_formatter
    (
        expr::stream
            << expr::attr< unsigned int >("LineID") << ": ["
            << expr::c_decor[ expr::stream << expr::attr< std::string >("Tag") ] << "] "
            << expr::c_ascii_decor[ expr::stream << expr::message ]
    );

###### General character decorator

    #include <boost/log/expressions/formatters/char_decorator.hpp>

This decorator allows the user to define his own character replacement mapping in one of the two forms. The first form is a range of `std::pair`s of strings (which can be C-style strings or ranges of characters, including `std::string`s). The strings in the `first` elements of pairs will be replaced with the `second` elements of the corresponding pair.

    std::array< std::pair< const char*, const char* >, 3 > shell_escapes =
    {
        { "\"", "\\\"" },
        { "'",  "\\'" },
        { "$",  "\\$" }
    };

    sink->set_formatter
    (
        expr::char_decor(shell_escapes)
        [
            expr::stream << expr::message
        ]
    );

The second form is two same-sized sequences of strings; the first containing the search patterns and the second - the corresponding replacements.

    std::array< const char*, 3 > shell_patterns =
    {
        "\"", "'", "$"
    };
    std::array< const char*, 3 > shell_replacements =
    {
        "\\\"", "\\'", "\\$"
    };

    sink->set_formatter
    (
        expr::char_decor(shell_patterns, shell_replacements)
        [
            expr::stream << expr::message
        ]
    );

In both cases the patterns are not interpreted and are sought in the formatted characters in the original form.