### Filtering revisited

We've already touched filtering in the previous sections but we barely scratched the surface. Now that we are able to add attributes to log records and set up sinks, we can build however complex filtering we need. Let's consider this example:

    BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
    BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)

    void init()
    {
        // Setup the common formatter for all sinks
        logging::formatter fmt = expr::stream
            << std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
            << ": <" << severity << ">\t"
            << expr::if_(expr::has_attr(tag_attr))
               [
                   expr::stream << "[" << tag_attr << "] "
               ]
            << expr::smessage;

        // Initialize sinks
        typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
        boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

        sink->locked_backend()->add_stream(
            boost::make_shared< std::ofstream >("full.log"));

        sink->set_formatter(fmt);

        logging::core::get()->add_sink(sink);

        sink = boost::make_shared< text_sink >();

        sink->locked_backend()->add_stream(
            boost::make_shared< std::ofstream >("important.log"));

        sink->set_formatter(fmt);

        sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));

        logging::core::get()->add_sink(sink);

        // Add attributes
        logging::add_common_attributes();
    }

See the complete code.

In this sample we initialize two sinks - one for the complete log file and the other for important messages only. Both sinks will be writing to text files with the same log record format, which we initialize first and save to the `fmt` variable. The `formatter` type is a type-erased function object with the formatter calling signature; in many respects it can be viewed similar to `boost::function` or `std::function` except that it is never empty. There is also `a similar function object` for filters.

Notably, the formatter itself contains a filter here. As you can see, the format contains a conditional part that is only present when log records contain the "Tag" attribute. The `has_attr` predicate checks whether the record contains the "Tag" attribute value and controls whether it is put into the file or not. We used the attribute keyword to specify the name and type of the attribute for the predicate, but it is also possible to specify them in the `has_attr` call site. Conditional formatters are explained in more details here.

Further goes the initialization of the two sinks. The first sink does not have any filter, which means it will save every log record to the file. We call `set_filter` on the second sink to only save log records with severity no less than `warning` or having a "Tag" attribute with value "IMPORTANT_MESSAGE". As you can see, the filter syntax resembles usual C++ very much, especially when attribute keywords are used.

Like with formatters, it is also possible to use custom functions as filters. Boost.Phoenix can be very helpful in this case as its `bind` implementation is compatible with attribute placeholders. The previous example can be modified in the following way:

    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()));

        // ...
    }

As you can see, the custom formatter receives attribute values wrapped into the `value_ref` template. This wrapper contains an optional reference to the attribute value of the specified type; the reference is valid if the log record contains the attribute value of the required type. The relational operators used in `my_filter` can be applied unconditionally because they will automatically return `false` if the reference is not valid. The rest is done with the `bind` expression which will recognize the `severity` and `tag_attr` keywords and extract the corresponding values before passing them to `my_filter`.

| Note

Because of limitations related to the integration with Boost.Phoenix (see #7996), it is required to explicitly specify the fallback policy in case if the attribute value is missing, when attribute keywords are used with `phoenix::bind` or `phoenix::function`. In the example above, this is done by calling `or_none`, which results in an empty `value_ref` if the value is not found. In other contexts this policy is the default. There are other policies that can be used instead.

You can try how this works by compiling and running the test.