### Writing your own sources

    #include <boost/log/sources/threading_models.hpp>
    #include <boost/log/sources/basic_logger.hpp>

You can extend the library by developing your own sources and, for that matter, ways of collecting log data. Basically, you have two choices of how to start: you can either develop a new logger feature or design a whole new type of source. The first approach is good if all you need is to tweak the functionality of the existing loggers. The second approach is reasonable if the whole mechanism of collecting logs by the provided loggers is unsuitable for your needs.

##### Creating a new logger feature

Every logger provided by the library consists of a number of features that can be combined with each other. Each feature is responsible for a single and independent aspect of the logger functionality. For example, loggers that provide the ability to assign severity levels to logging records include the `severity` feature. You can implement your own feature and use it along with the ones provided by the library.

A logger feature should follow these basic requirements:

- A logging feature should be a class template. It should have at least one template parameter type (let's name it `BaseT`).
- The feature must publicly derive from the `BaseT` template parameter.
- The feature must be default-constructible and copy-constructible.
- The feature must be constructible with a single argument of a templated type. The feature may not use this argument itself, but it should pass this argument to the `BaseT` constructor.

These requirements allow composition of a logger from a number of features derived from each other. The root class of the features hierarchy will be the `basic_logger` class template instance. This class implements most of the basic functionality of loggers, like storing logger-specific attributes and providing the interface for log message formatting. The hierarchy composition is done by the `basic_composite_logger` class template, which is instantiated on a sequence of features (don't worry, this will be shown in an example in a few moments). The constructor with a templated argument allows initializing features with named parameters, using the Boost.Parameter library.

A logging feature may also contain internal data. In that case, to maintain thread safety for the logger, the feature should follow these additional guidelines:

1. Usually there is no need to introduce a mutex or another synchronization mechanism in each feature. Moreover, it is advised not to do so, because the same feature can be used in both thread-safe and not thread-safe loggers. Instead, features should use the threading model of the logger as a synchronization primitive, similar to how they would use a mutex. The threading model is accessible through the `get_threading_model` method, defined in the `basic_logger` class template.
2. If the feature has to override `*_unlocked` methods of the protected interface of the `basic_logger` class template (or the same part of the base feature interface), the following should be considered with regard to such methods:
   - The public methods that eventually call these methods are implemented by the `basic_composite_logger` class template. These implementations do the necessary locking and then pass control to the corresponding `_unlocked` method of the base features.
   - The thread safety requirements for these methods are expressed with lock types. These types are available as typedefs in each feature and the `basic_logger` class template. If the feature exposes a protected function `foo_unlocked`, it will also expose type `foo_lock`, which will express the locking requirements of `foo_unlocked`. The corresponding method `foo` in the `basic_composite_logger` class template will use this typedef in order to lock the threading model before calling `foo_unlocked`.
   - Feature constructors don't need locking, and thus there's no need for lock types for them.
3. The feature may implement a copy constructor. The argument of the constructor is already locked with a shared lock when the constructor is called. Naturally, the feature is expected to forward the copy constructor call to the `BaseT` class.
4. The feature need not implement an assignment operator. The assignment will be automatically provided by the `basic_composite_logger` class instance. However, the feature may provide a `swap_unlocked` method that will swap contents of this feature and the method argument, and call similar method in the `BaseT` class. The automatically generated assignment operator will use this method, along with copy constructor.

In order to illustrate all these lengthy recommendations, let's implement a simple logger feature. Suppose we want our logger to be able to tag individual log records. In other words, the logger has to temporarily add an attribute to its set of attributes, emit the logging record, and then automatically remove the attribute. Somewhat similar functionality can be achieved with scoped attributes, although the syntax may complicate wrapping it into a neat macro:

    // We want something equivalent to this
    {
        BOOST_LOG_SCOPED_LOGGER_TAG(logger, "Tag", "[GUI]");
        BOOST_LOG(logger) << "The user has confirmed his choice";
    }

Let's declare our logger feature:

    template< typename BaseT >
    class record_tagger_feature :
        public BaseT
    {
    public:
        // Let's import some types that we will need. These imports should be public,
        // in order to allow other features that may derive from record_tagger to do the same.
        typedef typename BaseT::char_type char_type;
        typedef typename BaseT::threading_model threading_model;

    public:
        // Default constructor. Initializes m_Tag to an invalid value.
        record_tagger_feature();
        // Copy constructor. Initializes m_Tag to a value, equivalent to that.m_Tag.
        record_tagger_feature(record_tagger_feature const& that);
        // Forwarding constructor with named parameters
        template< typename ArgsT >
        record_tagger_feature(ArgsT const& args);

        // The method will require locking, so we have to define locking requirements for it.
        // We use the strictest_lock trait in order to choose the most restricting lock type.
        typedef typename logging::strictest_lock<
            boost::lock_guard< threading_model >,
            typename BaseT::open_record_lock,
            typename BaseT::add_attribute_lock,
            typename BaseT::remove_attribute_lock
        >::type open_record_lock;

    protected:
        // Lock-less implementation of operations
        template< typename ArgsT >
        logging::record open_record_unlocked(ArgsT const& args);
    };

    // A convenience metafunction to specify the feature
    // in the list of features of the final logger later
    struct record_tagger :
        public boost::mpl::quote1< record_tagger_feature >
    {
    };

| the feature should derive from other features or the basic_logger class

You can see that we use the `strictest_lock` template in order to define lock types that would fulfill the base class thread safety requirements for methods that are to be called from the corresponding methods of `record_tagger_feature`. The `open_record_lock` definition shows that the `open_record_unlocked` implementation for the `record_tagger_feature` feature requires exclusive lock (which `lock_guard` is) for the logger, but it also takes into account locking requirements of the `open_record_unlocked`, `add_attribute_unlocked` and `remove_attribute_unlocked` methods of the base class, because it will have to call them. The generated `open_record` method of the final logger class will make use of this typedef in order to automatically acquire the corresponding lock type before forwarding to the `open_record_unlocked` methods.

Actually, in this particular example, there was no need to use the `strictest_lock` trait, because all our methods require exclusive locking, which is already the strictest one. However, this template may come in handy, should you use shared locking.

The implementation of the public interface becomes quite trivial:

    template< typename BaseT >
    record_tagger_feature< BaseT >::record_tagger_feature()
    {
    }

    template< typename BaseT >
    record_tagger_feature< BaseT >::record_tagger_feature(record_tagger_feature const& that) :
        BaseT(static_cast< BaseT const& >(that))
    {
    }

    template< typename BaseT >
    template< typename ArgsT >
    record_tagger_feature< BaseT >::record_tagger_feature(ArgsT const& args) : BaseT(args)
    {
    }

Now, since all locking is extracted into the public interface, we have the most of our feature logic to be implemented in the protected part of the interface. In order to set up tag value in the logger, we will have to introduce a new Boost.Parameter keyword. Following recommendations from that library documentation, it's better to introduce the keyword in a special namespace:

    namespace my_keywords {

        BOOST_PARAMETER_KEYWORD(tag_ns, tag)

    }

Opening a new record can now look something like this:

    template< typename BaseT >
    template< typename ArgsT >
    logging::record record_tagger_feature< BaseT >::open_record_unlocked(ArgsT const& args)
    {
        // Extract the named argument from the parameters pack
        std::string tag_value = args[my_keywords::tag | std::string()];

        logging::attribute_set& attrs = BaseT::attributes();
        logging::attribute_set::iterator tag = attrs.end();
        if (!tag_value.empty())
        {
            // Add the tag as a new attribute
            std::pair<
                logging::attribute_set::iterator,
                bool
            > res = BaseT::add_attribute_unlocked("Tag",
                attrs::constant< std::string >(tag_value));
            if (res.second)
                tag = res.first;
        }

        // In any case, after opening a record remove the tag from the attributes
        BOOST_SCOPE_EXIT_TPL((&tag)(&attrs))
        {
            if (tag != attrs.end())
                attrs.erase(tag);
        }
        BOOST_SCOPE_EXIT_END

        // Forward the call to the base feature
        return BaseT::open_record_unlocked(args);
    }

Here we add a new attribute with the tag value, if one is specified in call to `open_record`. When a log record is opened, all attribute values are acquired and locked after the record, so we remove the tag from the attribute set with the Boost.ScopeExit block.

Ok, we got our feature, and it's time to inject it into a logger. Assume we want to combine it with the standard severity level logging. No problems:

    template< typename LevelT = int >
    class my_logger :
        public src::basic_composite_logger<
            char,
            my_logger< LevelT >,
            src::single_thread_model,
            src::features<
                src::severity< LevelT >,
                record_tagger
            >
        >
    {
        // The following line will automatically generate forwarding constructors that
        // will call to the corresponding constructors of the base class
        BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE(my_logger)
    };

| character type for the logger
| final logger type
| the logger does not perform thread synchronization; use `multi_thread_model` to declare a thread-safe logger
| the list of features we want to combine

As you can see, creating a logger is a quite simple procedure. The `BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE` macro you see here is for mere convenience purpose: it unfolds into a default constructor, copy constructor, assignment operator and a number of constructors to support named arguments. For non-template loggers there is a similar `BOOST_LOG_FORWARD_LOGGER_MEMBERS` macro.

Assuming we have defined severity levels like this:

    enum severity_level
    {
        normal,
        warning,
        error
    };

we can now use our logger as follows:

    void manual_logging()
    {
        my_logger< severity_level > logger;

        logging::record rec = logger.open_record((keywords::severity = normal, my_keywords::tag = "GUI"));
        if (rec)
        {
            logging::record_ostream strm(rec);
            strm << "The user has confirmed his choice";
            strm.flush();
            logger.push_record(boost::move(rec));
        }
    }

All this verbosity is usually not required. One can define a special macro to make the code more concise:

    #define LOG_WITH_TAG(lg, sev, tg) \
        BOOST_LOG_WITH_PARAMS((lg), (keywords::severity = (sev))(my_keywords::tag = (tg)))

    void logging_function()
    {
        my_logger< severity_level > logger;

        LOG_WITH_TAG(logger, normal, "GUI") << "The user has confirmed his choice";
    }

See the complete code.

##### Guidelines for designers of standalone logging sources

In general, you can implement new logging sources the way you like, the library does not mandate any design requirements on log sources. However, there are some notes regarding the way log sources should interact with logging core.

1. Whenever a logging source is ready to emit a log record, it should call the `open_record` in the core. The source-specific attributes should be passed into that call. During that call the core allocates resources for the record being made and performs filtering.
2. If the call to `open_record` returned a valid log record, then the record has passed the filtering and is considered to be opened. The record may later be either confirmed by the source by subsequently calling `push_record` or withdrawn by destroying it.
3. If the call to `open_record` returned an invalid (empty) log record, it means that the record has not been opened (most likely due to filtering rejection). In that case the logging core does not hold any resources associated with the record, and thus the source must not call `push_record` for that particular logging attempt.
4. The source may subsequently open more than one record. Opened log records exist independently from each other.