Phil
Phil

Reputation: 6164

How to stop all asynchronous sinks when using Boost.Log

I am using Boost.Log asynchronous sinks (see Asynchronous sink frontend). To have a proper shutdown, one must gracefully stop and flush the feeding of records to the async sinks. The core has methods for adding and removing sinks, but there does not seem to be a way for a client to get sinks or visit them. The documentation has a stop_logging method,

void stop_logging(boost::shared_ptr< sink_t >& sink)
{
    boost::shared_ptr< logging::core > core = logging::core::get();

    // Remove the sink from the core, so that no records are passed to it
    core->remove_sink(sink);

    // Break the feeding loop
    sink->stop();

    // Flush all log records that may have left buffered
    sink->flush();

    sink.reset();
}

but it takes a specific sink_t type. The asynchronous_sink frontend class has template arguments for the sink backend and queuing strategy.

  template<typename SinkBackendT, 
           typename QueueingStrategyT = unbounded_fifo_queue> 
    class asynchronous_sink;

I will have several different kinds of sinks, so I would like to have a generic container that holds them, so I can simple iterate over it and call stop_logging for each sink in the container.

This is really a general question about C++ templated data structures that I need to address because of the interface provided in Boost.Log. What is a good data structure to use to keep track of asynchronous sinks I have added to the Boost.Log core? I need one so I can call stop_logging on shutdown.

My first simple-minded approach is to have a vector of boost::any objects. But that is rather cumbersome and inelegant. I suspect that a reasonable approach would be to have a vector of function objects that are lambda methods calling stop_logging. But I get lost in the template types and do not know how to do this.

I appreciate any help. Thanks!

Upvotes: 2

Views: 2022

Answers (1)

Andrey Semashev
Andrey Semashev

Reputation: 10614

The most direct solution would be to have a container with function objects, as you suggested. For example:

std::vector< std::function< void() > > stop_functions;

// For every asynchronous sink you add
stop_functions.emplace_back([sink]()
{
    sink->flush();
    sink->stop();
});

In this example sink can be a pointer to any instance of asynchronous_sink that you happen to add to the logging core. When your application terminates you just have to call all the saved functions in the container:

for (auto& stop : stop_functions)
    stop();

However, there is a Boost.Signals2 library that allows to simplify this process a little. You can create a signal and then connect the functions to it like this:

boost::signals2::signal< void() > stop_signal;

// For every asynchronous sink you do
stop_signal.connect([sink]()
{
    sink->flush();
    sink->stop();
});

Then by invoking the signal you'll be calling every connected function object, effectively stopping every sink.

stop_signal();

In either case, you can use std::bind or any other means to create the function objects you like. The essential part is to save the pointer to the sink frontend, along with its type, into the function object.

Upvotes: 4

Related Questions