Search code examples
c++loggingboostboost-logboost-logging

Simultaneous Logging to Console and File using Boost


I need help to initialize the boost logging framework to simultaneously log to both a named log file and also the console - (the named log file will not require periodic rotation or any of that fancy setup per many of the boost tutorials).

The logging text should go to both sinks simultaneously, however I need to format the console output slightly differently (as it will be viewed by a user.) I was able to get the basics of logging to 2 separate sinks working using the boost example code. It is overly complex for what I need to do and it is really confusing as far as accessing the appropriate logger is concerned. All I need to do is have time stamped messages sent to the log file and have the same information without the times-stamps or newlines send to the console log (putting in new lines explicitly only as I would typically do with << std::endl). I would really like to stick with boost's logging framework as it gives the flexibility to expand in the future.

With the example, I tried tail -f the log files - however the log output does not appear to get auto flushed after each log entry. Although this is not very important for the file logs, this would be critical for the console output stream as it represents live activity that a user will be monitoring.

Any help or even better, some really simple sample code to get the basics working be much appreciated.

The way I setup my logging (per the link above) is as shown below, I would like to replace one of these registered sinks with a console logger - but I am not sure how. I expect that a console logger will have auto flush.

// Setup the common formatter for all sinks
logging::formatter fmt = expr::stream
    << std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
    << ": <" << severity << ">\t"
    << expr::if_(expr::has_attr(tag_attr))
    [
        expr::stream << "[" << tag_attr << "] "
    ]
    << expr::smessage;

// Initialize sinks
typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;

boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>("full.log"));
sink->set_formatter(fmt);
// register the full log sink
logging::core::get()->add_sink(sink);

sink = boost::make_shared<text_sink>();
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>("important.log"));
// sink->set_formatter(fmt); (I removed this to not have any special formatting hopefully)
sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));
// register the important log sink
logging::core::get()->add_sink(sink);

// Add attributes
logging::add_common_attributes();

Solution

  • Here is some sample code that utilizes Boost-Log's global logger. I call init_term() to initialize my terminal logger and init_logfile() to initialize my logfile.

    Note the auto_flush(true) call

    // Logging macro
    #define LOG(level) BOOST_LOG_SEV(global_logger::get(), level)
    // Initializing global boost::log logger
    typedef boost::log::sources::severity_channel_logger_mt<
        severity_level, std::string> global_logger_type;
    
    BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(global_logger, global_logger_type)
    {
        return global_logger_type(boost::log::keywords::channel = "global_logger");
    }
    
    // Initialize terminal logger
    void init_term()
    {
        // create sink to stdout
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
        sink->locked_backend()->add_stream(
            boost::shared_ptr<std::ostream>(&out, boost::empty_deleter()));
    
        // flush
        sink->locked_backend()->auto_flush(true);
    
        // format sink
        sink->set_formatter
        (
            /// TODO add your desired formatting
        );
    
        // filter
        // TODO add any filters
    
        // register sink
        bl::core::get()->add_sink(sink);
    }
    
    // Initialize logfile
    void init_logfile(const std::string& logfilename)
    {
        // create sink to logfile
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
        sink->locked_backend()->add_stream(
            boost::make_shared<std::ofstream>(logfilename.c_str()));
    
        // flush
        sink->locked_backend()->auto_flush(true);
    
        // format sink
        sink->set_formatter
        (
            /// TODO add your desired formatting
        );
    
        // filter
        // TODO add any filters
    
        // register sink
        bl::core::get()->add_sink(sink);
    }