johnco3 johnco3 - 3 months ago 52
C++ Question

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 (file and console) 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 way 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 streaming operations). 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();

Answer

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);
}