5

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();
Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
johnco3
  • 2,401
  • 4
  • 35
  • 67

1 Answers1

4

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);
}
Patrizio Bertoni
  • 2,582
  • 31
  • 43
Dimitris Dakopoulos
  • 683
  • 1
  • 6
  • 18
  • Dimitris, thanks, do you have the relevant #includes and namespace aliases. Also I forgot to mention that I am developing for Visual Studio 2013, I tend to get tons of warnings that need to be suppressed via the #pragma warning(disable : 4510 4610) (wrapped inside a #pragma warning(push/pop) - but I still get lots of warnings. – johnco3 Jun 17 '14 at 16:21
  • Actually I just figured out how to get it to compile via your "Boost Log run-time optimization" which I upvoted. Very useful. Also regarding the autoflush - this is not so important for my log file - but it is for the terminal. In fact I will have 2 separate log files (used for different logging purposes - so in effect 3 log sinks - 2 files and a console) - the thing I cannot get my head around is how to get LOG("something") to direct its output the an appropriate destination output. I think it has to do with filters but it is not obvious, I'm sure I will need some sort of attr to help – johnco3 Jun 17 '14 at 16:46
  • @johnco3, I think the solution lies in filters. If you want `LOG(LEVEL)` to write to a specific sink first you need to pass the `severity_level` to all sinks initialization functions that you don't want it to exist and then filter it e.g. `sink->set_filter(boost::log::expressions::attr("Severity") == LEVEL);` – Dimitris Dakopoulos Jun 18 '14 at 07:17
  • I agree, Yesterday, I wrote a Logger class from scratch to try to solve this problem with partial success - using multiple channel_loggers. The logger class 3 channel_logger members initialized with a channel name. The code is very similar to: ttps://github.com/boostorg/log/blob/master/example/doc/sources_net_connection_dynamic_chan.cpp the thing thath confuses me is why it is necessary to call BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) on line 33 - I thought channel was a predefined attribute associated with channel loggers. – johnco3 Jun 18 '14 at 16:46