Friday, January 24, 2014

Example to Use Boost.Log

The following code is an example that illustrates how to use Boost.Log.

Several Notes:

  • In the example, debug_output_backend (e.g. DebugView, or Visual Studio Output Pane) is used. It support multi-process logging. It is only available on Windows. On linux, syslog_backend is a good alternative.
  • Log level control is implemented using a global varable g_log_level and a boost log filter function log_level_filter.
  • Messages can be logged by send them to log streams, e.g. LOG(), LOG_ERROR.
  • All the items that output to log stream should support operator<<(std::ostream&) or operator<<(boost::log::record_ostream&)

See also Boost.Log

#include <iostream>
#include <iomanip>
#include <string>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sinks/debug_output_backend.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/phoenix/bind/bind_function_object.hpp>
#include <boost/log/support/date_time.hpp>

using namespace std;
using namespace boost;
using namespace boost::log;

namespace {

    // log severity level
    enum log_level {
        Debug,
        Info,
        Warning,
        Error
    };

    // logger type with severity level and multithread support
    typedef sources::severity_logger_mt<log_level> logger_t;

    // windows debugger output (e.g. DbgView) sink type with synchronization support
    typedef sinks::synchronous_sink<sinks::debug_output_backend> sink_t;

    // global variables
    log_level g_log_level = Info;  // log level
    logger_t* g_logger; // logger

    // define the keywords to use in filter and formatter
    BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", log_level)
    BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", 
        attributes::local_clock::value_type)
    BOOST_LOG_ATTRIBUTE_KEYWORD(processid, "ProcessID", 
        attributes::current_process_id::value_type)
    BOOST_LOG_ATTRIBUTE_KEYWORD(threadid, "ThreadID", 
        attributes::current_thread_id::value_type)

    // the filter used to filter log records according to level
    bool log_level_filter(value_ref<log_level, tag::severity> const& level) {
        return level >= g_log_level;
    }

    // stream output support for log_level
    std::ostream& operator<<(std::ostream& stream, log_level level) {
        static const char* strings[] = { "debug", " info", " warn", "error" };
        int l = static_cast<size_t>(level);
        if (l >= 0 && l < sizeof(strings) / sizeof(strings[0]))
            stream << strings[l];
        else
            stream << l;
        return stream;
    }

    void initialize_logger() {
        // create a logger
        g_logger = new logger_t();

        boost::shared_ptr<core> core = core::get();

        // add attributes
        core->add_global_attribute("TimeStamp", attributes::local_clock());
        core->add_global_attribute("ProcessID", attributes::current_process_id());
        core->add_global_attribute("ThreadID", attributes::current_thread_id());

        // add level filter
        core->set_filter(phoenix::bind(&log_level_filter, severity.or_none()));

        // create a debug output sink
        boost::shared_ptr<sink_t> sink(new sink_t());

        // sink formatter
        sink->set_formatter(expressions::stream
            << "[" << processid << "]"
            << "[" << threadid << "]"
            << "[" << expressions::format_date_time(
                                        timestamp, "%Y-%m-%d %H:%M:%S") << "]"
            << "[" << severity << "] "
            << expressions::smessage);

        // add sink
        core->add_sink(sink);
    }

    void cleanup_logger() {
        if (g_logger) {
            delete g_logger;
            g_logger = nullptr;
        }
    }

    // macros to get log streams
    #define LOG_DEBUG()     BOOST_LOG_STREAM_SEV(*g_logger, Debug) \ 
        << __FUNCTION__ << "(): "
    #define LOG_INFO()      BOOST_LOG_STREAM_SEV(*g_logger, Info) \ 
        << __FUNCTION__ << "(): "
    #define LOG_WARNING()   BOOST_LOG_STREAM_SEV(*g_logger, Info) \ 
        << __FUNCTION__ << "(): "
    #define LOG_ERROR()     BOOST_LOG_STREAM_SEV(*g_logger, Error) \ 
        << __FUNCTION__ << "(): "
    #define LOG()           LOG_INFO()
}

void doSomething() {
    LOG() << "Log message from doSomethng()";
}

int main(int argc, char** argv) {
    initialize_logger();

    LOG() << "Application started";
    LOG_ERROR() << "An error occured";
    doSomething();

    LOG_DEBUG() << "This message won't be log if g_log_level >= Info";
    g_log_level = Debug;
    LOG_DEBUG() << "This message will be log if g_log_level >= Debug";

    cleanup_logger();
    return 0;
}

1 comment: