Boost C++ Libraries

PrevUpHomeNext

Utilities

String literals
Type dispatchers
Predefined type sequences
Value reference wrapper
Log record ordering
Exception handlers
Output manipulators
Interprocess communication tools
Simplified library initialization tools
#include <boost/log/utility/string_literal.hpp>

String literals are used in several places throughout the library. However, this component can be successfully used outside of the library in users' code. It is header-only and does not require linking with the library binary. String literals can improve performance significantly if there is no need to modify stored strings. What is also important, since string literals do not dynamically allocate memory, it is easier to maintain exception safety when using string literals instead of regular strings.

The functionality is implemented in the basic_string_literal class template, which is parametrized with the character and character traits, similar to std::basic_string. There are also two convenience typedefs provided: string_literal and wstring_literal, for narrow and wide character types, respectively. In order to ease string literal construction in generic code there is also a str_literal function template that accepts a string literal and returns a basic_string_literal instance for the appropriate character type.

String literals support interface similar to STL strings, except for string modification functions. However, it is possible to assign to or clear string literals, as long as only string literals involved. Relational and stream output operators are also supported.

#include <boost/log/utility/type_dispatch/type_dispatcher.hpp>

Type dispatchers are used throughout the library in order to work with attribute values. Dispatchers allow acquiring the stored attribute value using the Visitor concept. The most notable places where the functionality is used are filters and formatters. However, this mechanism is orthogonal to attributes and can be used for other purposes as well. Most of the time users won't need to dig into the details of type dispatchers, but this information may be useful for those who intend to extend the library and wants to understand what's under the hood.

Every type dispatcher supports the type_dispatcher interface. When an attribute value needs to be extracted, this interface is passed to the attribute value object, which then tries to acquire the callback for the actual type of the value. All callbacks are objects of the [class_type_dispatcher_callback] class template, instantiated on the actual type of the value. If the dispatcher is able to consume the value of the requested type, it must return a non-empty callback object. When (and if) the corresponding callback is acquired, the attribute value object only has to pass the contained value to its operator ().

Happily, there is no need to write type dispatchers from scratch. The library provides two kinds of type dispatchers that implement the type_dispatcher and [class_type_dispatcher_callback] interfaces and encapsulate the callback lookup.

Static type dispatcher
#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp>

Static type dispatchers are used when the set of types that needs to be supported for extraction is known at compile time. The static_type_dispatcher class template is parametrized with an MPL type sequence of types that need to be supported. The dispatcher inherits from the type_dispatcher interface which provides the get_callback method for acquiring the function object to invoke on the stored value. All you need to do is provide a visitor function object to the dispatcher at construction point and invoke the callback when dispatching the stored value:

// Base interface for the custom opaque value
struct my_value_base
{
    virtual ~my_value_base() {}
    virtual bool dispatch(logging::type_dispatcher& dispatcher) const = 0;
};

// A simple attribute value
template< typename T >
struct my_value :
    public my_value_base
{
    T m_value;

    explicit my_value(T const& value) : m_value(value) {}

    // The function passes the contained type into the dispatcher
    bool dispatch(logging::type_dispatcher& dispatcher) const
    {
        logging::type_dispatcher::callback< T > cb = dispatcher.get_callback< T >();
        if (cb)
        {
            cb(m_value);
            return true;
        }
        else
            return false;
    }
};

// Value visitor for the supported types
struct print_visitor
{
    typedef void result_type;

    // Implement visitation logic for all supported types
    void operator() (int const& value) const
    {
        std::cout << "Received int value = " << value << std::endl;
    }
    void operator() (double const& value) const
    {
        std::cout << "Received double value = " << value << std::endl;
    }
    void operator() (std::string const& value) const
    {
        std::cout << "Received string value = " << value << std::endl;
    }
};

// Prints the supplied value
bool print(my_value_base const& val)
{
    typedef boost::mpl::vector< int, double, std::string > types;

    print_visitor visitor;
    logging::static_type_dispatcher< types > disp(visitor);

    return val.dispatch(disp);
}

See the complete code.

Dynamic type dispatcher
#include <boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp>

If the set of types that have to be supported is not available at compile time, the dynamic_type_dispatcher class is there to help. One can use its register_type method to add support for a particular type. The user has to pass a function object along with the type, this functor will be called when a visitor for the specified type is invoked. Considering the my_value from the code sample for static type dispatcher is intact, the code can be rewritten as follows:

// Visitor functions for the supported types
void on_int(int const& value)
{
    std::cout << "Received int value = " << value << std::endl;
}

void on_double(double const& value)
{
    std::cout << "Received double value = " << value << std::endl;
}

void on_string(std::string const& value)
{
    std::cout << "Received string value = " << value << std::endl;
}

logging::dynamic_type_dispatcher disp;

// The function initializes the dispatcher object
void init_disp()
{
    // Register type visitors
    disp.register_type< int >(&on_int);
    disp.register_type< double >(&on_double);
    disp.register_type< std::string >(&on_string);
}

// Prints the supplied value
bool print(my_value_base const& val)
{
    return val.dispatch(disp);
}

See the complete code.

Of course, complex function objects, like those provided by Boost.Bind, are also supported.

#include <boost/log/utility/type_dispatch/standard_types.hpp>
#include <boost/log/utility/type_dispatch/date_time_types.hpp>

One may notice that when using type dispatchers and defining filters and formatters it may be convenient to have some predefined type sequences to designate frequently used sets of types. The library provides several such sets.

Table 1.6. Standard types (standard_types.hpp)

Type sequence

Meaning

integral_types

All integral types, including bool, character and 64 bit integral types, if available

floating_point_types

Floating point types

numeric_types

Includes integral_types and floating_point_types

string_types

Narrow and wide string types. Currently only includes STL string types and string literals.


There are also a number of time-related type sequences available:

Table 1.7. Time-related types (date_time_types.hpp)

Type sequence

Meaning

native_date_time_types

All types defined in C/C++ standard that have both date and time portions

boost_date_time_types

All types defined in Boost.DateTime that have both date and time portions

date_time_types

Includes native_date_time_types and boost_date_time_types

native_date_types

All types defined in C/C++ standard that have date portion. Currently equivalent to native_date_time_types.

boost_date_types

All types defined in Boost.DateTime that have date portion

date_types

Includes native_date_types and boost_date_types

native_time_types

All types defined in C/C++ standard that have time portion. Currently equivalent to native_date_time_types.

boost_time_types

All types defined in Boost.DateTime that have time portion. Currently equivalent to boost_date_time_types.

time_types

Includes native_time_types and boost_time_types

native_time_duration_types

All types defined in C/C++ standard that are used to represent time duration. Currently only includes double, as the result type of the difftime standard function.

boost_time_duration_types

All time duration types defined in Boost.DateTime

time_duration_types

Includes native_time_duration_types and boost_time_duration_types

boost_time_period_types

All time period types defined in Boost.DateTime

time_period_types

Currently equivalent to boost_time_period_types


#include <boost/log/utility/value_ref.hpp>

The value_ref class template is an optional reference wrapper which is used by the library to refer to the stored attribute values. To a certain degree it shares features of Boost.Optional and Boost.Variant components.

The template has two type parameters. The first is the referred type. It can also be specified as a Boost.MPL type sequence, in which case the value_ref wrapper may refer to either type in the sequence. In this case, the which method will return the index of the referred type within the sequence. The second template parameter is an optional tag type which can be used to customize formatting behavior. This tag is forwarded to the to_log manipulator when the wrapper is put to a basic_formatting_ostream stream, which is used by the library for record formatting. For an example see how attribute value extraction is implemented:

void print_value_multiple_types(logging::attribute_value const& attr)
{
    // Define the set of expected types of the stored value
    typedef boost::mpl::vector< int, std::string > types;

    // Extract a reference to the stored value
    logging::value_ref< types > val = logging::extract< types >(attr);

    // Check the result
    if (val)
    {
        std::cout << "Extraction succeeded" << std::endl;
        switch (val.which())
        {
        case 0:
            std::cout << "int: " << val.get< int >() << std::endl;
            break;

        case 1:
            std::cout << "string: " << val.get< std::string >() << std::endl;
            break;
        }
    }
    else
        std::cout << "Extraction failed" << std::endl;
}

See the complete code.

The value_ref wrapper also supports applying a visitor function object to the referred object. This can be done by calling one of the following methods:

  • apply_visitor. This method should only be used on a valid (non-empty) reference. The method returns the visitor result.
  • apply_visitor_optional. The method checks if the reference is valid and applies the visitor to the referred value if it is. The method returns the visitor result wrapped into boost::optional which will be filled only if the reference is valid.
  • apply_visitor_or_default. If the reference is valid, the method applies the visitor on the referred value and returns its result. Otherwise the method returns a default value passed as the second argument.
[Note] Note

Regardless of the method used, the visitor function object must define the result_type typedef. Polymorphic visitors are not supported as this would complicate the value_ref interface too much. This requirement also precludes free functions and C++11 lambda functions from being used as visitors. Please, use Boost.Bind or similar wrappers in such cases.

Here is an example of applying a visitor:

struct hash_visitor
{
    typedef std::size_t result_type;

    result_type operator() (int val) const
    {
        std::size_t h = val;
        h = (h << 15) + h;
        h ^= (h >> 6) + (h << 7);
        return h;
    }

    result_type operator() (std::string const& val) const
    {
        std::size_t h = 0;
        for (std::string::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
            h += *it;

        h = (h << 15) + h;
        h ^= (h >> 6) + (h << 7);
        return h;
    }
};

void hash_value(logging::attribute_value const& attr)
{
    // Define the set of expected types of the stored value
    typedef boost::mpl::vector< int, std::string > types;

    // Extract the stored value
    logging::value_ref< types > val = logging::extract< types >(attr);

    // Check the result
    if (val)
        std::cout << "Extraction succeeded, hash value: " << val.apply_visitor(hash_visitor()) << std::endl;
    else
        std::cout << "Extraction failed" << std::endl;
}

#include <boost/log/utility/record_ordering.hpp>

There are cases when log records need to be ordered. One possible use case is storing records in a container or a priority queue. The library provides two types of record ordering predicates out of the box:

Abstract record ordering

The abstract_ordering class allows application of a quick opaque ordering. The result of this ordering is not stable between different runs of the application and in general cannot be predicted before the predicate is applied, however it provides the best performance. The abstract_ordering class is a template that is specialized with an optional predicate function that will be able to compare const void* pointers. By default an std::less equivalent is used.

// A set of unique records
std::set< logging::record_view, logging::abstract_ordering< > > m_Records;

This kind of ordering can be useful if the particular order of log records is not important but nevertheless some order is required.

Attribute value based ordering

This kind of ordering is implemented with the attribute_value_ordering class and is based on the attribute values attached to the record. The predicate will seek for an attribute value with the specified name in both records being ordered and attempt to compare the attribute values.

// Ordering type definition
typedef logging::attribute_value_ordering<
    int     // attribute value type
> ordering;

// Records organized into a queue based on the "Severity" attribute value
std::priority_queue<
    logging::record_view,
    std::vector< logging::record_view >,
    ordering
> m_Records(ordering("Severity"));

Like the abstract_ordering, attribute_value_ordering also accepts the second optional template parameter, which should be the predicate to compare attribute values (ints in the example above). By default, an std::less equivalent is used.

You can also use the make_attr_ordering generator function to automatically generate the attribute_value_ordering instance based on the attribute value name and the ordering function. This might be useful if the ordering function has a non-trivial type, like the ones Boost.Bind provides.

#include <boost/log/utility/exception_handler.hpp>

The library provides exception handling hooks in different places. Tools, defined in this header, provide an easy way of implementing function objects suitable for such hooks.

An exception handler is a function object that accepts no arguments. The result of the exception handler is ignored and thus should generally be void. Exception handlers are called from within catch sections by the library, therefore in order to reacquire the exception object it has to rethrow it. The header defines an exception_handler template functor that does just that and then forwards the exception object to a unary user-defined functional object. The make_exception_handler function can be used to simplify the handler construction. All expected exception types should be specified explicitly in the call, in the order they would appear in the catch sections (i.e. from most specific ones to the most general ones).

struct my_handler
{
    typedef void result_type;

    void operator() (std::runtime_error const& e) const
    {
        std::cout << "std::runtime_error: " << e.what() << std::endl;
    }
    void operator() (std::logic_error const& e) const
    {
        std::cout << "std::logic_error: " << e.what() << std::endl;
        throw;
    }
};

void init_exception_handler()
{
    // Setup a global exception handler that will call my_handler::operator()
    // for the specified exception types
    logging::core::get()->set_exception_handler(logging::make_exception_handler<
        std::runtime_error,
        std::logic_error
    >(my_handler()));
}

As you can see, you can either suppress the exception by returning normally from operator() in the user-defined handler functor, or rethrow the exception, in which case it will propagate further. If it appears that the exception handler is invoked for an exception type that cannot be caught by any of the specified types, the exception will be propagated without any processing. In order to catch such situations, there exists the nothrow_exception_handler class. It invokes the user-defined functor with no arguments if it cannot determine the exception type.

struct my_handler_nothrow
{
    typedef void result_type;

    void operator() (std::runtime_error const& e) const
    {
        std::cout << "std::runtime_error: " << e.what() << std::endl;
    }
    void operator() (std::logic_error const& e) const
    {
        std::cout << "std::logic_error: " << e.what() << std::endl;
        throw;
    }
    void operator() () const
    {
        std::cout << "unknown exception" << std::endl;
    }
};

void init_exception_handler_nothrow()
{
    // Setup a global exception handler that will call my_handler::operator()
    // for the specified exception types. Note the std::nothrow argument that
    // specifies that all other exceptions should also be passed to the functor.
    logging::core::get()->set_exception_handler(logging::make_exception_handler<
        std::runtime_error,
        std::logic_error
    >(my_handler_nothrow(), std::nothrow));
}

It is sometimes convenient to completely suppress all exceptions at a certain library level. The make_exception_suppressor function creates an exception handler that simply does nothing upon exception being caught. For example, this way we can disable all exceptions from the logging library:

void init_logging()
{
    boost::shared_ptr< logging::core > core = logging::core::get();

    // Disable all exceptions
    core->set_exception_handler(logging::make_exception_suppressor());
}

The library provides a number of stream manipulators that may be useful in some contexts.

#include <boost/log/utility/manipulators/to_log.hpp>

The to_log function creates a stream manipulator that simply outputs the adopted value to the stream. By default its behavior is equivalent to simply putting the value to the stream. However, the user is able to overload the operator<< for the adopted value to override formatting behavior when values are formatted for logging purposes. This is typically desired when the regular operator<< is employed for other tasks (such as serialization) and its behavior is neither suitable for logging nor can be easily changed. For example:

std::ostream& operator<<
(
    std::ostream& strm,
    logging::to_log_manip< int > const& manip
)
{
    strm << std::setw(4) << std::setfill('0') << std::hex << manip.get() << std::dec;
    return strm;
}

void test_manip()
{
    std::cout << "Regular output: " << 1010 << std::endl;
    std::cout << "Log output: " << logging::to_log(1010) << std::endl;
}

The second streaming statement in the test_manip function will invoke our custom stream insertion operator which defines special formatting rules.

It is also possible to define different formatting rules for different value contexts as well. The library uses this feature to allow different formatting ruled for different attribute values, even if the stored value type is the same. To do so one has to specify an explicit template argument for to_log, a tag type, which will be embedded into the manipulator type and thus will allow to define different insertion operators:

struct tag_A;
struct tag_B;

std::ostream& operator<<
(
    std::ostream& strm,
    logging::to_log_manip< int, tag_A > const& manip
)
{
    strm << "A[" << manip.get() << "]";
    return strm;
}

std::ostream& operator<<
(
    std::ostream& strm,
    logging::to_log_manip< int, tag_B > const& manip
)
{
    strm << "B[" << manip.get() << "]";
    return strm;
}

void test_manip_with_tag()
{
    std::cout << "Regular output: " << 1010 << std::endl;
    std::cout << "Log output A: " << logging::to_log< tag_A >(1010) << std::endl;
    std::cout << "Log output B: " << logging::to_log< tag_B >(1010) << std::endl;
}

See the complete code.

[Note] Note

The library uses basic_formatting_ostream stream type for record formatting, so when customizing attribute value formatting rules the operator<< must use basic_formatting_ostream instead of std::ostream.

#include <boost/log/utility/manipulators/add_value.hpp>

The add_value function creates a manipulator that attaches an attribute value to a log record. This manipulator can only be used in streaming expressions with the basic_record_ostream stream type (which is the case when log record message is formatted). Since the message text is only formatted after filtering, attribute values attached with this manipulator do not affect filtering and can only be used in formatters and sinks themselves.

In addition to the value itself, the manipulator also requires the attribute name to be provided. For example:

// Creates a log record with attribute value "MyAttr" of type int attached
BOOST_LOG(lg) << logging::add_value("MyAttr", 10) << "Hello world!";
#include <boost/log/utility/manipulators/dump.hpp>

The dump function creates a manipulator that outputs binary contents of a contiguous memory region. This can be useful for logging some low level binary data, such as encoded network packets or entries of a binary file. The use is quite straightforward:

void on_receive(std::vector< unsigned char > const& packet)
{
    // Outputs something like "Packet received: 00 01 02 0a 0b 0c"
    BOOST_LOG(lg) << "Packet received: " << logging::dump(packet.data(), packet.size());
}

The manipulator also allows to limit the amount of data to be output, in case if the input data can be too large. Just specify the maximum number of bytes of input to dump as the last argument:

void on_receive(std::vector< unsigned char > const& packet)
{
    // Outputs something like "Packet received: 00 01 02 03 04 05 06 07 and 67 bytes more"
    BOOST_LOG(lg) << "Packet received: " << logging::dump(packet.data(), packet.size(), 8);
}

There is another manipulator called dump_elements for printing binary representation of non-byte array elements. The special manipulator for this case is necessary because the units of the size argument of dump can be confusing (is it in bytes or in elements?). Therefore dump will not compile when used for non-byte input data. dump_elements accepts the same arguments, and its size-related arguments always designate the number of elements to process.

void process(std::vector< double > const& matrix)
{
    // Note that dump_elements accepts the number of elements in the matrix, not its size in bytes
    BOOST_LOG(lg) << "Matrix dump: " << logging::dump_elements(matrix.data(), matrix.size());
}
[Tip] Tip

Both these manipulators can also be used with regular output streams, not necessarily loggers.

#include <boost/log/utility/manipulators/auto_newline.hpp>

Sometimes it can be useful to be able to insert a newline character in the output stream, but only if it hasn't been inserted as part of the previous output. For example, if a string may or may not end with a newline, and we cannot easily tell which one it is each time. The auto_newline manipulator can be used to ensure that all such strings are reliably terminated with a newline and there are no duplicate newline characters. The manipulator will insert a newline unless the last character inserted into the stream before it was a newline. Its use is similar to standard stream manipulators:

BOOST_LOG(lg) << "Parameter: " << param.name << ", value: " << param.value << logging::auto_newline;
[Note] Note

This manipulator inspects previous output to the stream, and therefore can only be used with Boost.Log streams based on basic_formatting_ostream.

#include <boost/log/utility/ipc/object_name.hpp>

In modern operating systems process-shared system resources are typically identified with names. Unfortunately, different systems have different requirements on the name syntax and allowed character set. Additionally, some systems offer support for namespaces in order to avoid name clashes. The object_name class is intended to hide these differences.

An object name can be constructed from a UTF-8 string identifier and a scope. A portable identifier can contain the following characters:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 . _ -
[Note] Note

The character set corresponds to POSIX Portable Filename Character Set.

Use of other characters may result in non-portable system-specific behavior.

The scope is identified with the object_name::scope enumeration:

  • global - The name has global scope; any process in the system has the potential to open the resource identified by the name. On some systems this scope may not be available or require elevated privileges.
  • user - The name is limited to processes running under the current user.
  • session - The name is limited to processes running in the current login session.
  • process_group - The name is limited to processes running in the current process group. Currently, on Windows all processes running in the current session are considered members of the same process group. This may change in future.

The scopes are not overlapping. For instance, if an object is created in the global scope, the object cannot be opened with the same name but in user's scope. Some of the scopes may require elevated privileges to create or open objects in.

[Warning] Warning

Object name scoping should not be considered a security measure. Objects may still be accessible by processes outside of their name scopes. The main purpose of the scopes is to avoid name clashes between different processes using object_name. Use access permissions for security control.

#include <boost/log/utility/ipc/reliable_message_queue.hpp>

The reliable_message_queue class implements a reliable one-way channel of passing messages from one or multiple writers to a single reader. The format of the messages is user-defined and must be consistent across all writers and the reader. The queue does not enforce any specific format of the messages, other than they should be supplied as a contiguous array of bytes. The queue internally uses a process-shared storage identified by an object name (the queue name).

The queue storage is organized as a fixed number of blocks of a fixed size. The block size must be an integer power of 2 and is expressed in bytes. Each written message, together with some metadata added by the queue, consumes an integer number of blocks. Each read message received by the reader releases the blocks allocated for that message. As such the maximum size of a message is slightly less than block size times capacity of the queue. For efficiency, it is recommended to choose block size large enough to accommodate most of the messages to be passed through the queue. The queue is considered empty when no messages are enqueued (all blocks are free). The queue is considered full at the point of enqueueing a message when there is not enough free blocks to accommodate the message.

The queue is reliable in that it will not drop successfully sent messages that are not received by the reader, other than the case when a non-empty queue is destroyed by the last user. If a message cannot be enqueued by the writer because the queue is full, the queue will act depending on the overflow policy specified at the queue creation:

  • block_on_overflow - Block the thread until there is enough space to enqueue the message or the operation is aborted by calling stop_local.
  • fail_on_overflow - Return an error code from the send operation. The error code is operation_result::no_space.
  • throw_on_overflow - Throw an exception from the send operation. The exception is capacity_limit_reached.

The policy is object local, i.e. different writers and the reader can have different overflow policies. Here is an example of writing to the message queue:

int main()
{
    typedef logging::ipc::reliable_message_queue queue_t;

    // Create a message_queue_type object that is associated with the interprocess
    // message queue named "ipc_message_queue".
    queue_t queue
    (
        keywords::name = logging::ipc::object_name(logging::ipc::object_name::user, "ipc_message_queue"),
        keywords::open_mode = logging::open_mode::open_or_create,  1
        keywords::capacity = 256,                                  2
        keywords::block_size = 1024,                               3
        keywords::overflow_policy = queue_t::fail_on_overflow      4
    );

    // Send a message through the queue
    std::string message = "Hello, Viewer!";
    queue_t::operation_result result = queue.send(message.data(), static_cast< queue_t::size_type >(message.size()));

    // See if the message was sent
    switch (result)
    {
    case queue_t::operation_result::succeeded:
        std::cout << "Message sent successfully" << std::endl;
        break;

    case queue_t::operation_result::no_space:
        std::cout << "Message could not be sent because the queue is full" << std::endl;
        break;

    case queue_t::operation_result::aborted:
        // This can happen is overflow_policy is block_on_overflow
        std::cout << "Message sending operation has been interrupted" << std::endl;
        break;
    }

    return 0;
}

1

create the queue, if not yet created

2

if the queue has to be created, allocate 256 blocks...

3

... of 1 KiB each for messages

4

if the queue is full, return error to the writer

See the complete code.

Typically, the queue would be used for sending log records to a different process. As such, instead of using the queue for writing directly, one would use a special sink backend for that. See text_ipc_message_queue_backend documentation.

Receiving messages from the queue is similar. Here is an example of a log viewer that receives messages from the queue and displays them on the console.

int main()
{
    try
    {
        typedef logging::ipc::reliable_message_queue queue_t;

        // Create a message_queue_type object that is associated with the interprocess
        // message queue named "ipc_message_queue".
        queue_t queue
        (
            keywords::name = logging::ipc::object_name(logging::ipc::object_name::user, "ipc_message_queue"),
            keywords::open_mode = logging::open_mode::open_or_create,
            keywords::capacity = 256,
            keywords::block_size = 1024,
            keywords::overflow_policy = queue_t::block_on_overflow
        );

        std::cout << "Viewer process running..." << std::endl;

        // Keep reading log messages from the associated message queue and print them on the console.
        // queue.receive() will block if the queue is empty.
        std::string message;
        while (queue.receive(message) == queue_t::succeeded)
        {
            std::cout << message << std::endl;

            // Clear the buffer for the next message
            message.clear();
        }
    }
    catch (std::exception& e)
    {
        std::cout << "Failure: " << e.what() << std::endl;
    }

    return 0;
}

See the complete code.

[Note] Note

The queue does not guarantee any particular order of received messages from different writer threads. Messages sent by a particular writer thread will be received in the order of sending.

A blocked reader or writer can be unblocked by calling stop_local. After this method is called, all threads blocked on this particular object are released and return operation_result::aborted. The other instances of the queue (in the current or other processes) are unaffected. In order to restore the normal functioning of the queue instance after the stop_local call the user has to invoke reset_local.

This part of the library is provided in order to simplify logging initialization and provide basic tools to develop user-specific initialization mechanisms. It is known that setup capabilities and preferences may vary widely from application to application, therefore the library does not attempt to provide a universal solution for this task. The provided tools are mostly intended to serve as a quick drop-in support for logging setup and a set of instruments to implement something more elaborate and more fitting users' needs.

Some of the features described in this section will require the separate library binary, with name based on "boost_log_setup" substring. This binary depends on the main library.

#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

The library provides a number of functions that simplify some common initialization procedures, like sink and commonly used attributes registration. This is not much functionality. However, it saves a couple of minutes of learning the library for a newcomer.

Logging to the application console is the simplest way to see the logging library in action. To achieve this, one can initialize the library with a single function call, like this:

int main(int, char*[])
{
    // Initialize logging to std::clog
    logging::add_console_log();

    // Here we go, we can write logs right away
    src::logger lg;
    BOOST_LOG(lg) << "Hello world!";

    return 0;
}

Pretty easy, isn't it? There is also the wadd_console_log function for wide-character console. If you want to put logs to some other standard stream, you can pass the stream to the add_console_log function as an argument. E.g. enabling logging to std::cout instead of std::clog would look like this:

logging::add_console_log(std::cout);

What's important, is that you can further manage the console sink if you save the shared_ptr to the sink that this function returns. This allows you to set up things like filter, formatter and auto-flush flag.

int main(int, char*[])
{
    // Initialize logging to std::clog
    boost::shared_ptr<
        sinks::synchronous_sink< sinks::text_ostream_backend >
    > sink = logging::add_console_log();

    sink->set_filter(expr::attr< int >("Severity") >= 3);
    sink->locked_backend()->auto_flush(true);

    // Here we go, we can write logs right away
    src::logger lg;
    BOOST_LOG(lg) << "Hello world!";

    return 0;
}

Similarly to console, one can use a single function call to enable logging to a file. All you have to do is to provide the file name:

int main(int, char*[])
{
    // Initialize logging to the "test.log" file
    logging::add_file_log("test.log");

    // Here we go, we can write logs right away
    src::logger lg;
    BOOST_LOG(lg) << "Hello world!";

    return 0;
}

The add_console_log and add_file_log functions do not conflict and may be combined freely, so it is possible to set up logging to the console and a couple of files, including filtering and formatting, in about 10 lines of code.

Lastly, there is an add_common_attributes function that registers two frequently used attributes: "LineID" and "TimeStamp". The former counts log record being made and has attribute value unsigned int. The latter, as its name implies, provides the current time for each log record, in the form of boost::posix_time::ptime (see Boost.DateTime). These two attributes are registered globally, so they will remain available in all threads and loggers. This makes the final version of our code sample look something like this:

int main(int, char*[])
{
    // Initialize sinks
    logging::add_console_log()->set_filter(expr::attr< int >("Severity") >= 4);

    logging::formatter formatter =
        expr::stream
            << expr::attr< unsigned int >("LineID") << ": "
            << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << " *"
            << expr::attr< int >("Severity") << "* "
            << expr::message;

    logging::add_file_log("complete.log")->set_formatter(formatter);

    boost::shared_ptr<
        sinks::synchronous_sink< sinks::text_ostream_backend >
    > sink = logging::add_file_log("essential.log");
    sink->set_formatter(formatter);
    sink->set_filter(expr::attr< int >("Severity") >= 1);

    // Register common attributes
    logging::add_common_attributes();

    // Here we go, we can write logs
    src::logger lg;
    BOOST_LOG(lg) << "Hello world!";

    return 0;
}
#include <boost/log/utility/setup/filter_parser.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>

Filter and formatter parsers allow constructing filters and formatters from a descriptive string. The function parse_filter is responsible for recognizing filters and parse_formatter - for recognizing formatters.

In the case of filters the string is formed of a sequence of condition expressions, interconnected with boolean operations. There are two operations supported: conjunction (designated as "&" or "and") and disjunction ("|" or "or"). Each condition itself may be either a single condition or a sub-filter, taken in round brackets. Each condition can be negated with the "!" sign or "not" keyword. The condition, if it's not a sub-filter, usually consists of an attribute name enclosed in percent characters ("%"), a relation keyword and an operand. The relation and operand may be omitted, in which case the condition is assumed to be the requirement of the attribute presence (with any type).

filter:
    condition { op condition }

op:
    &
    and
    |
    or

condition:
    !condition
    not condition
    (filter)
    %attribute_name%
    %attribute_name% relation operand

relation:
    >
    <
    =
    !=
    >=
    <=
    begins_with
    ends_with
    contains
    matches

Below are some examples of filters:

Table 1.8. Examples of filters

Filter string

Description

%Severity%

The filter returns true if an attribute value with name "Severity" is found in a log record.

%Severity% > 3

The filter returns true if an attribute value with name "Severity" is found and it is greater than 3. The attribute value must be of one of the integral types.

!(%Ratio% > 0.0 & %Ratio% <= 0.5)

The filter returns true if an attribute value with name "Ratio" of one of the floating point types is not found or it is not between 0 and 0.5.

%Tag% contains "net" or %Tag% contains "io" and not %StatFlow%

The filter returns true if an attribute value with name "Tag" is found and contains words "net" or "io" and if an attribute value "StatFlow" is not found. The "Tag" attribute value must be of one of the string types, the "StatFlow" attribute value type is not considered.


The formatter string syntax is even simpler and pretty much resembles Boost.Format format string syntax. The string is interpreted as a template which can contain attribute names enclosed with percent signs ("%"). The corresponding attribute values will replace these placeholders when the formatter is applied. The placeholder "%Message%" will be replaced with the log record text. For instance, the following formatter string:

[%TimeStamp%] *%Severity%* %Message%

will make log records look like this:

[2008-07-05 13:44:23] *0* Hello world
[Note] Note

Previous releases of the library also supported the "%_%" placeholder for the message text. This placeholder is deprecated now, although it still works for backward compatibility. Its support will be removed in future releases.

It must be noted that by default the library only supports those attribute value types which are known at the library build time. User-defined types will not work properly in parsed filters and formatters until registered in the library. It is also possible to override formatting rules of the known types, including support for additional formatting parameters in the string template. More on this is available in the Extending the library section.

[Note] Note

The parsed formatters and filters are generally less optimal than the equivalent ones written in code with template expressions. This is because of two reasons: (*) the programmer usually knows more about types of the attribute values that may be involved in formatting or filtering and (*) the compiler has a better chance to optimize the formatter or filter if it is known in compile time. Therefore, when performance matters, it is advised to avoid parsed filters and formatters.

#include <boost/log/utility/setup/settings.hpp>
#include <boost/log/utility/setup/from_settings.hpp>

The headers define components for library initialization from a settings container. The settings container is basically a set of named parameters divided into sections. The container is implemented with the basic_settings class template. There are several constraints on how parameters are stored in the container:

  • Every parameter must reside in a section. There can be no parameters that do not belong to a section.
  • Parameters must have names unique within the section they belong to. Parameters from different sections may have the same name.
  • Sections can nest. When read from a file or accessed from the code, section names can express arbitrary hierarchy by separating the parent and child section names with '.' (e.g. "[Parent.Child.ChildChild]").
  • Sections must have names unique within the enclosing section (or global scope, if the section is top level).

So basically, settings container is a layered associative container, with string keys and values. In some respect it is similar to Boost.PropertyTree, and in fact it supports construction from boost::ptree. The supported parameters are described below.

[Tip] Tip

In the tables below, the CharT type denotes the character type that is used with the settings container.

Table 1.9. Section "Core". Logging core settings.

Parameter

Format

Description

Filter

Filter string as described here

Global filter to be installed to the core. If not specified, the global filter is not set.

DisableLogging

"true" or "false"

If true, results in calling set_logging_enabled(false) on the core. By default, value false is assumed.


Sink settings are divided into separate subsections within the common top-level section "Sinks" - one subsection for each sink. The subsection names denote a user-defined sink name. For example, "MyFile".

[Note] Note

Previous versions of the library also supported top-level sections starting with the "Sink:" prefix to describe sink parameters. This syntax is deprecated now, although it still works when parsing a settings file for backward compatibility. The parser will automatically put these sections under the "Sinks" top-level section in the resulting settings container. Support for this syntax will be removed in future releases.

Table 1.10. Sections under the "Sinks" section. Common sink settings.

Parameter

Format

Description

Destination

Sink target, see description

Sink backend type. Mandatory parameter. May have one of these values: Console, TextFile, Syslog. On Windows the following values are additionally supported: SimpleEventLog, Debugger. Also, user-defined sink names may also be supported if registered by calling register_sink_factory. See this section on how to add support for new sinks.

Filter

Filter string as described here

Sink-specific filter. If not specified, the filter is not set.

Asynchronous

"true" or "false"

If true, the asynchronous sink frontend will be used. Otherwise the synchronous sink frontend will be used. By default, value false is assumed. In single-threaded builds this parameter is not used, as unlocked sink frontend is always used.


Besides the common settings that all sinks support, some sink backends also accept a number of specific parameters. These parameters should be specified in the same section.

Table 1.11. "Console" sink settings

Parameter

Format

Description

Format

Format string as described here

Log record formatter to be used by the sink. If not specified, the default formatter is used.

AutoNewline

"Disabled", "AlwaysInsert" or "InsertIfMissing"

Controls whether the backend should automatically insert a trailing newline after every log record, see auto_newline_mode. If not specified, the default value is "InsertIfMissing".

AutoFlush

"true" or "false"

Enables or disables the auto-flush feature of the backend. If not specified, the default value false is assumed.


Table 1.12. "TextFile" sink settings

Parameter

Format

Description

FileName

File name pattern

The active file name pattern for the sink backend. This parameter is mandatory.

TargetFileName

File name pattern

The target file name pattern for the sink backend. If not specified, active file name is preserved after rotation.

Format

Format string as described here

Log record formatter to be used by the sink. If not specified, the default formatter is used.

AutoNewline

"Disabled", "AlwaysInsert" or "InsertIfMissing"

Controls whether the backend should automatically insert a trailing newline after every log record, see auto_newline_mode. If not specified, the default value is "InsertIfMissing".

AutoFlush

"true" or "false"

Enables or disables the auto-flush feature of the backend. If not specified, the default value false is assumed.

Append

"true" or "false"

Enables or disables appending to the existing file instead of overwriting it. If not specified, the default value false is assumed.

RotationSize

Unsigned integer

File size, in bytes, upon which file rotation will be performed. If not specified, no size-based rotation will be made.

RotationInterval

Unsigned integer

Time interval, in seconds, upon which file rotation will be performed. See also the RotationTimePoint parameter and the note below.

RotationTimePoint

Time point format string, see below

Time point or a predicate that detects at what moment of time to perform log file rotation. See also the RotationInterval parameter and the note below.

EnableFinalRotation

"true" or "false"

Enables or disables final file rotation on sink destruction, which typically happens on program termination. If not specified, the default value true is assumed.

Target

File system path to a directory

Target directory name, in which the rotated files will be stored. If this parameter is specified, rotated file collection is enabled. Otherwise the feature is not enabled and all corresponding parameters are ignored.

MaxSize

Unsigned integer

Total size of files in the target directory, in bytes, upon which the oldest file will be deleted. If not specified, no size-based file cleanup will be performed.

MinFreeSpace

Unsigned integer

Minimum free space in the target directory, in bytes, upon which the oldest file will be deleted. If not specified, no space-based file cleanup will be performed.

MaxFiles

Unsigned integer

Total number of files in the target directory, upon which the oldest file will be deleted. If not specified, no count-based file cleanup will be performed.

ScanForFiles

"All" or "Matching"

Mode of scanning for old files in the target directory, see scan_method. If not specified, no scanning will be performed.


[Warning] Warning

The text file sink uses Boost.Filesystem internally, which may cause problems on process termination. See here for more details.

The time-based rotation can be set up with one of the two parameters: RotationInterval or RotationTimePoint. Not more than one of these parameters should be specified for a given sink. If none is specified, no time-based rotation will be performed.

The RotationTimePoint parameter should have one of the following formats, according to the Boost.DateTime format notation:

  • "%H:%M:%S". In this case, file rotation will be performed on a daily basis, at the specified time. For example, "12:00:00".
  • "%a %H:%M:%S" or "%A %H:%M:%S". File rotation takes place every week, on the weekday specified in the long or short form, at the specified time. For example, "Saturday 09:00:00".
  • "%d %H:%M:%S". File rotation takes place every month, on the specified day of month, at the specified time. For example, "01 23:30:00".

Table 1.13. "Syslog" sink settings

Parameter

Format

Description

Format

Format string as described here

Log record formatter to be used by the sink. If not specified, the default formatter is used.

LocalAddress

An IP address

Local address to initiate connection to the syslog server. If not specified, the default local address will be used.

TargetAddress

An IP address

Remote address of the syslog server. If not specified, the local address will be used.


Table 1.14. "SimpleEventLog" sink settings

Parameter

Format

Description

Format

Format string as described here

Log record formatter to be used by the sink. If not specified, the default formatter is used.

LogName

A string

Log name to write events into. If not specified, the default log name will be used.

LogSource

A string

Log source to write events from. If not specified, the default source will be used.

Registration

"Never", "OnDemand" or "Forced"

Mode of log source registration in Windows registry, see registration_mode. If not specified, on-demand registration will be performed.


The user is free to fill the settings container from whatever settings source he needs. The usage example is below:

logging::settings setts;

setts["Core"]["Filter"] = "%Severity% >= warning";
setts["Core"]["DisableLogging"] = false;

// Subsections can be referred to with a single path
setts["Sinks.Console"]["Destination"] = "Console";
setts["Sinks.Console"]["Filter"] = "%Severity% >= critical";
setts["Sinks.Console"]["Format"] = "%TimeStamp% [%Severity%] %Message%";
setts["Sinks.Console"]["AutoFlush"] = true;

// ...as well as the individual parameters
setts["Sinks.File.Destination"] = "TextFile";
setts["Sinks.File.FileName"] = "MyApp_%3N.log";
setts["Sinks.File.AutoFlush"] = true;
setts["Sinks.File.RotationSize"] = 10 * 1024 * 1024; // 10 MiB
setts["Sinks.File.Format"] = "%TimeStamp% [%Severity%] %Message%";

logging::init_from_settings(setts);

See the complete code.

[Note] Note

Initialization from settings does not automatically create any attributes or loggers, the application developer is still responsible for creating those. It can be said that loggers and attributes define the data that is exported by the application and the settings only describe how that data is going to be presented to the application user.

The settings reader also can be extended to support custom sink types. See the Extending the library section for more information.

#include <boost/log/utility/setup/from_stream.hpp>

Support for configuration files is a frequently requested feature of the library. And despite the fact there is no ultimately convenient and flexible format of the library settings, the library provides preliminary support for this feature. The functionality is implemented with a simple function init_from_stream, which accepts a std::istream and reads the library settings from it. The function then passes on the read settings to the init_from_settings function, described above. Therefore the parameter names and their meaning is the same as for the init_from_settings function.

The settings format is quite simple and widely used. Below is the description of syntax and parameters.

# Comments are allowed. Comment line begins with the '#' character
# and spans until the end of the line.

# Logging core settings section. May be omitted if no parameters specified within it.
[Core]
DisableLogging=false
Filter="%Severity% > 3"

# Sink settings sections
[Sinks.MySink1]

# Sink destination type
Destination=Console

# Sink-specific filter. Optional, by default no filter is applied.
Filter="%Target% contains \"MySink1\""

# Formatter string. Optional, by default only log record message text is written.
Format="<%TimeStamp%> - %Message%"

# The flag shows whether the sink should be asynchronous
Asynchronous=false

# Enables automatic stream flush after each log record.
AutoFlush=true

Here's the usage example:

int main(int, char*[])
{
    // Read logging settings from a file
    std::ifstream file("settings.ini");
    logging::init_from_stream(file);

    return 0;
}

See a more complete example.


PrevUpHomeNext