Search code examples
c++boost-log

Boost Log Setting Rotation and Max File Size Results in 2nd Sink Being Added, when Remove_All_Sinks() is called prior


Using Boost log for logging purposes (duh) - and I am successful in initialization:

BOOST_LOG_GLOBAL_LOGGER_INIT(logger, logger_t) {
    logger_t lg;
    logging::add_common_attributes();
    boost::shared_ptr< file_sink > sink(new file_sink(
        boost::log::keywords::file_name = "appLog_%N.log",
        boost::log::keywords::rotation_size = 2 * 1024 * 1024,
        boost::log::keywords::max_size = 10 * 1024 * 1024,
        boost::log::keywords::scan_method = 
        boost::log::sinks::file::scan_method::scan_matching,
        boost::log::keywords::open_mode = std::ios_base::app,
        boost::log::keywords::auto_flush = true
    ));

    sink->set_formatter(
        expr::stream
        << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%dT%H:%M:%S.%fz")
        << ": [" << logging::trivial::severity
        << "]\t" << expr::smessage
    );

    sink->locked_backend()->set_file_collector(boost::log::sinks::file::make_collector(
        boost::log::keywords::target = "",                      /*< the target directory >*/
        boost::log::keywords::max_size = 10 * 1024 * 1024,          /*< maximum total size of the stored files, in bytes >*/
        boost::log::keywords::min_free_space = 1 * 1024 * 1024,   /*< minimum free space on the drive, in bytes >*/
        boost::log::keywords::max_files = 10                       /*< maximum number of stored files >*/
    ));

    sink->locked_backend()->scan_for_files(boost::log::sinks::file::scan_method::scan_matching, true);

    logging::core::get()->add_sink(sink);

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );

    return lg;
}

However, after reading in and processing the application's configuration file, which includes values specifying rotation_size and max_size for logging, I am having difficulty removing all sinks, before creating a new sink with the specified values for rotation_size and max_size simply adds a sink on top of the existing sink which was initialized globally (which results in each message being logged twice).

The function for updating the sizes of the log is as follows:

void setLoggerSizes(int rotationSize, int maxSize)
{
    //INFO << "";

    logging::core::get()->remove_all_sinks();

    logging::add_common_attributes();

    boost::shared_ptr< file_sink > sink(new file_sink(
        boost::log::keywords::file_name = "appLog_%N.log",
        boost::log::keywords::rotation_size = rotationSize * 1024 * 1024,
        boost::log::keywords::max_size = maxSize * 1024 * 1024,
        boost::log::keywords::scan_method = boost::log::sinks::file::scan_method::scan_matching,
        boost::log::keywords::open_mode = std::ios_base::app,
        boost::log::keywords::auto_flush = true
    ));

    sink->set_formatter(
        expr::stream
        << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%dT%H:%M:%S.%fz")
        << ": [" << logging::trivial::severity
        << "]\t" << expr::smessage
    );

    sink->locked_backend()->set_file_collector(
        boost::log::sinks::file::make_collector(
            boost::log::keywords::target = "",
            boost::log::keywords::max_size = maxSize * 1024 * 1024,
            boost::log::keywords::min_free_space = 1 * 1024 * 1024
        )
    );

    sink->locked_backend()->scan_for_files();   

    logging::core::get()->add_sink(sink);
}

Yet, if I add the INFO << ""; in within the beginning of the function, this seems to force the removal of all sinks and when adding the sink with the correct file sizes, results in a single sink (and consequently only one copy of messages to be passed to the log).

Thus, my questions are:

  1. Why is this happening with INFO << "";? It does not make any logical sense as to why adding that line would truly force a removal of all sinks which is called in the next line - and consequently result in a single copy of messages to be logged. Whereas, not including the INFO line, it is like the logging::core::get()->remove_all_sinks(); call is ignored, and the sink which is added with the correct file sizes is added on top of the initialized sink, and results in two copies of each message to be logged.
  2. How do I properly set the rotation and max sizes, if I am not doing so now, while maintaining a global logger?

For reference, this is the Logger.h file:

#pragma once

#include <boost/log/expressions.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>

#define FATAL  BOOST_LOG_SEV(logger::get(), boost::log::trivial::fatal)
#define WARNING  BOOST_LOG_SEV(logger::get(), boost::log::trivial::warning)
#define INFO BOOST_LOG_SEV(logger::get(), boost::log::trivial::info)
#define DEBUG BOOST_LOG_SEV(logger::get(), boost::log::trivial::debug)
#define TRACE BOOST_LOG_SEV(logger::get(), boost::log::trivial::trace)

typedef boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> logger_t;
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_file_backend > file_sink;

BOOST_LOG_GLOBAL_LOGGER(logger, logger_t)

void setLoggerSizes(int rotationSize, int maxSize);

Solution

  • The problem you're seeing is most likely because of the order of the execution of the logging statements in your program.

    The body of the BOOST_LOG_GLOBAL_LOGGER_INIT and the similar BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT macro is executed once, when the global logger is requested by the program for the very first time (see here). So, since you request the logger from within your INFO macro, that body is executed before you call remove_all_sinks and further initialize the logging library. When you comment that line, that initialization is deferred until your first executed logging statement, which, presumably, happens after setLoggerSizes returns. Hence, remove_all_sinks has no effect since there are no sinks to remove at the point of that call.

    The _INIT macros are really intended to initialize the logger, not the whole logging library (i.e. sinks, filters and what not). You can initialize the library from those macros, but, as you've found out, the usefullness of this is fairly limited (basically, it will likely not work as intended unless you have a single global logger, which is also the only logger you use in your whole application, and you don't need to update the logging configuration once it is initialized). The correct way to do this is to initialize the logging library separately in a function that you will call some time early in your main, and use _INIT macros to only initialize loggers.