Boost C++ Libraries

PrevUpHomeNext

Writing your own attributes

#include <boost/log/attributes/attribute.hpp>
#include <boost/log/attributes/basic_attribute_value.hpp>

Developing your own attributes is quite simple. Generally, you need to do the following:

  1. Define what will be the attribute value. Most likely, it will be a piece of constant data that you want to participate in filtering and formatting. Encapsulate this data into a class that derives from the attribute_value interface. This object will have to implement the dispatch method that will extract the stored data (or, in other words, the stored value) to a type dispatcher.
  2. Define how attribute values are going to be produced. In a corner case the values do not need to be produced (like in the case of the constant attribute provided by the library), but often there is some logic that needs to be invoked to acquire the attribute value. This logic has to be concentrated in a class derived from the attribute interface, more precisely - in the get_value method. You can think of it as an attribute value factory.

While designing an attribute, one has to strive to make it as independent from the values it produces, as possible. The attribute can be called from different threads concurrently to produce a value. Once produced, the attribute value can be used several times by the library (maybe even concurrently), it can outlive the attribute object where it was created, and several attribute values produced by the same attribute can exist simultaneously.

Each attribute value is considered independent from other attribute values or the attribute itself, from the point of view of the library. That said, it is still possible to implement attributes that are also attribute values, and thus optimize performance. This is possible if it fulfils either of the following:

As a special case for the second point, it is possible to store attribute values (or their parts) in thread-specific storage. However, in that case the user has to implement the detach_from_thread method of the attribute value properly. The result of this method - another attribute value - must be independent from the thread it is being called in, but its stored value should be equivalent to the original attribute value. This method will be called by the library when the attribute value passes to a thread that is different from the thread where it was created. As of this moment, this will only happen in the case of asynchronous logging sinks.

But in the vast majority of cases attribute values must be self-contained objects with no dependencies on other entities. In fact, this case is so common that the library provides a ready to use attribute value class template basic_attribute_value. The template has to be instantiated on the stored value type, and the stored value has to be provided to the attribute value constructor. For example, this is how to implement an attribute that will return system uptime in seconds:

class system_uptime :
    public logging::attribute
{
    typedef attrs::basic_attribute_value< unsigned int > attribute_value_type;

public:
    boost::shared_ptr< attribute_value > get_value()
    {
        unsigned int up;

#if defined(BOOST_WINDOWS)
        up = GetTickCount() / 1000;
#else // assume other platforms provide sysinfo function
        struct sysinfo info;
        if (sysinfo(&info) != 0)
            throw std::runtime_error("Could not acquire uptime");
        up = info.uptime;
#endif

        return boost::shared_ptr< attribute_value >(new attribute_value_type(up));
    }
};

PrevUpHomeNext