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:
has_attr
filter to first verify
that the required attribute value is present.
std::nothrow
argument to the attr
placeholder. In this case the filter will silently return false
as the result of such unsuccessful
filtering.