Boost C++ Libraries

PrevUpHomeNext

Filters

Attribute presence filter
Generic attribute placeholder

As it was pointed out in tutorial, filters are implemented as Lambda-like 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.

#include <boost/log/filters/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.

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

public:
    // Destructor
    ~my_stat_accumulator()
    {
        // Display the accumulated data
        for (StatInfo_t::const_iterator it = m_StatInfo.begin();
            it != m_StatInfo.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(values_view_type const& attributes, string_type const& message)
    {
        // First, acquire statistic information stream name
        values_view_type::const_iterator itName = attributes.find("StatisticStream");
        if (itName != attributes.end())
        {
            boost::optional< std::string const& > name =
                itName->second.get< std::string >();
            if (name)
            {
                // Next, get the statistic value change
                values_view_type::const_iterator itChange = attributes.find("Change");
                if (itChange != attributes.end())
                {
                    boost::optional< int const& > change = itChange->second.get< int >();
                    if (change)
                    {
                        // Accumulate the statistic data
                        m_StatInfo[name.get()] += change.get();
                    }
                }
            }
        }
    }
};

// The function registers two sinks - one for statistic information,
// and another one for other records
void foo()
{
    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 > LogSink_t;
    boost::shared_ptr< LogSink_t > sink(new LogSink_t(backend));
    // All records that don't have a "StatisticStream" attribute attached
    // will go to the "test.log" file
    sink->set_filter(!flt::has_attr("StatisticStream"));

    core->add_sink(sink);

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

    core->add_sink(stat_sink);
}

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

void bar()
{
    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);
}

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 the technique of extracting the attribute value from the view - we need to both check that the needed attribute value is found in the view and that it has the required type. There is also a mechanism of attribute value extractors that can simplify this, especially when an attribute can have a number of types.

#include <boost/log/filters/attr.hpp>

Unlike has_attr, the attr construct is not a complete filter by itself. Instead, it is a placeholder that can take part in a complete filter expressions like this:

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

The line above registers a composite filter that consists of two elementary subfilters: the first one checks the severity level, and the second checks the channel name. Like has_attr, the attr placeholder accepts an expected attribute value type or set of types in an MPL-compatible type sequence. However, attribute value type is not optional with attr, like it is with has_attr.

Besides generating lambda expressions, the attr placeholder allows constructing filters with its member functions. There are currently two such members that can be used with any type:

  • is_in_range(T const& lower, T const& upper) generates a filter that returns true if the attribute value x of type T satisfies condition lower <= x < upper. For example:
sink->set_filter
(
    // drops all records that have level below 3 or greater than 4
    flt::attr< int >("Severity").is_in_range(3, 5)
);
  • satisfies(FunT const& fun) allows injection of a user-defined unary predicate fun into the filter. The predicate should accept an attribute value as an argument and return a value, convertible to bool. The result of the filter will be equivalent to the result of the predicate. For example:
bool check_severity_level(int level);

sink->set_filter
(
    flt::attr< int >("Severity").satisfies(&check_severity_level)
);

The attr placeholder has advanced support for string-typed attribute values (that have type std::string or std::wstring, that is). For string attribute values there are also available the following member functions:

  • begins_with(T const& s), ends_with(T const& s) and contains(T const& s). 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 s, respectively. The string comparison is case sensitive.
sink->set_filter
(
    // selects only records that are related to Russian web domains
    flt::attr< std::string >("Domain").ends_with(".ru")
);
  • matches(T const& regex) allows to set up a filter based on Boost.Regex or Boost.Xpressive regular expressions matching mechanism (note that you also have to include the appropriate header from the boost/log/support folder). The filter returns true if the attribute value matches the regex expression.
sink->set_filter
(
    flt::attr< std::string >("Domain").matches(boost::regex("www\\..*\\.ru"))
);

It is possible that the filter doesn't find the required attribute value in the given log record. By default, the filter will throw an exception in this case. If this behavior is not desired, there are two ways to change it:

  1. Use the has_attr filter to first verify that the required attribute value is present.
  2. Use an additional second std::nothrow argument to the attr placeholder. In this case the filter will silently return false as the result of such unsuccessful filtering.

PrevUpHomeNext