|  | 
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(); }
        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. Fundamentally, a filter function must support the following signature:
bool (logging::attribute_value_set const& attrs);
        When the filter is called, attrs
        will contain a complete set of attribute values, which can be used to decide
        whether the log record should be passed or suppressed. If the filter returns
        true, the log record will be
        constructed and further processed by sinks. Otherwise, the record will be
        discarded.
      
        Boost.Phoenix
        can be very helpful in constructing filters. It allows to automate extraction
        of attribute values from the attrs
        set 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 filter receives attribute values as separate arguments,
        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]](../../../../../../doc/src/images/note.png) | 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  | 
You can try how this works by compiling and running the test.