boostboost-log

Boost BOOST_LOG_SEV with custom attributes


I need a Boost logger which logs to console (and later on to a file), with following parameters: "[%TimeStamp%] [%Severity%] [%File%(%Line%)] %Message%". I have read the Boost.Log v2 docs and got some "inspiraton" from other places but I can't really get this to work.

// cswlogger.h
#pragma once
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>


BOOST_LOG_GLOBAL_LOGGER(sysLogger,
boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>);


class CswLogger 
{
public:
    /// Init with default info level logging
    static void init(boost::log::trivial::severity_level level = boost::log::trivial::info);

    /// Disable logging
    static void disable();
};

#define LOG_LOG_LOCATION(LOGGER, LEVEL, ARG)            \
  BOOST_LOG_SEV(LOGGER, boost::log::trivial::LEVEL)     \
    << boost::log::add_value("Line", __LINE__)          \
    << boost::log::add_value("File", __FILE__) << ARG


/// System Log macros.
/// TRACE < DEBUG < INFO < WARN < ERROR < FATAL
#define LOG_TRACE(ARG) LOG_LOG_LOCATION(sysLogger::get(), trace, ARG);
#define LOG_DEBUG(ARG) LOG_LOG_LOCATION(sysLogger::get(), debug, ARG);
#define LOG_INFO(ARG)  LOG_LOG_LOCATION(sysLogger::get(), info, ARG);
#define LOG_WARN(ARG)  LOG_LOG_LOCATION(sysLogger::get(), warning, ARG);
#define LOG_ERROR(ARG) LOG_LOG_LOCATION(sysLogger::get(), error, ARG);
#define LOG_FATAL(ARG) LOG_LOG_LOCATION(sysLogger::get(), fatal, ARG);

And source file:

//  cswlogger.cpp

#include "cswlogger.h"

#include <boost/log/core.hpp>
#include <boost/log/common.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/settings.hpp>
#include <boost/log/sinks/sync_frontend.hpp>

BOOST_LOG_GLOBAL_LOGGER_DEFAULT(sysLogger,
    boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level>);


void CswLogger::init(boost::log::trivial::severity_level level) 
{
    boost::log::add_console_log
    (
        std::clog,
        boost::log::keywords::format = "[%TimeStamp%] [%Severity%] [%File%(%Line%)] %Message%"
    );

    boost::log::core::get()->set_filter
    (
        boost::log::trivial::severity >= level
    );

    // Indicate start of logging
    LOG_INFO("Log Start");
}

void CswLogger::disable() 
{
    boost::log::core::get()->set_logging_enabled(false);
}

main.cpp

#include "cswlogger.h"
CswLogger::init();
LOG_INFO("This is a info trace");

The output of this will be: "[ ] [ ] main.cpp(3) This is a info trace"

The Timestamp and Severity paramaters are missing. Is it possible to use the "BOOST_LOG_SEV" log macro and add custom log parameters or do I need to use another approach?


Solution

  • First, the TimeStamp attribute is missing in the output because you haven't added it to the logging core. You can add it as described here:

    boost::log::core::get()->add_global_attribute(
        "TimeStamp",
        boost::log::attributes::local_clock());
    

    Or you could add it as one of the commonly used attributes by calling add_common_attributes.

    Next, the Severity attribute is present, it is provided by the logger. However, the value type of that attribute (which is boost::log::trivial::severity_level) is not supported by default for filters and formatters parsed from strings. You can solve this in two ways.

    First, you can switch to the manual setup of the logging sinks, which will allow you to set the filter and formatter for the sink as a lambda expression. This way you will be able to provide the attribute type to the formatter. For example:

    BOOST_LOG_ATTRIBUTE_KEYWORD(a_timestamp, "TimeStamp", boost::log::attributes::local_clock::value_type)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", boost::log::trivial::severity_level)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_file, "File", std::string)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_line, "Line", int)
    
    typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > sink_t;
    auto sink = boost::make_shared< sink_t >();
    sink->set_formatter(boost::log::expressions::ostream
        << "[" << a_timestamp << "] "
        << "[" << a_severity << "] "
        << "[" << a_file << "(" << a_line << ")] "
        << boost::log::expressions::message);
    
    boost::log::core::get()->add_sink(sink);
    

    Alternatively, you can register the formatter and filter factories for the Severity attribute so that the formatter and filter parsers are able to associate that attribute with the boost::log::trivial::severity_level type. This is described in detail here. In your case, you can just add this call before any formatters are parsed (i.e. before the add_console_log call):

    boost::log::register_simple_formatter_factory<
        boost::log::trivial::severity_level, char >("Severity");