Boost Log 2.0 : empty Severity level in logs
Asked Answered
S

5

25

I'm using Boost-Log 2.0, which has some differences from version 1, and I have a hard time outputting the "Severity" attribute.

I'm using the "Boost.Format-style" formatters

"%TimeStamp% [%Uptime%] (%LineID%) <%Severity%>: %Message%"

TimeStamp, LineID, and Message are common_attributes. Uptime is an attribute I added using attrs::timer(). I thought that Severity was automatically added when using severity_logger, but apparently it isn't and that's my problem. I get empty severities, eg:

2013-Apr-06 19:21:52.408974 [00:00:00.001337] (3) <>: A warning severity message

Notice the empty <>. I've tried to add severity using register_simple_formatter_factory but then I get the compiler error :

error: no matching function for call to ‘register_simple_formatter_factory(const char [9])’

and I don't get why.

Here's my code :

#include <iostream>


#include <boost/log/common.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/record_ostream.hpp>

#include <boost/log/attributes.hpp>

using namespace std;

namespace logging = boost::log;
namespace expr = boost::log::expressions;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace keywords = boost::log::keywords;

enum severity_level
{
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    CRITICAL
};

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt< severity_level> )

// The formatting logic for the severity level
template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (
    std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl)
{
    static const char* const str[] =
    {
        "DEBUG",
        "INFO",
        "WARNING",
        "ERROR",
        "CRITICAL"
    };
    if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str)))
        strm << str[lvl];
    else
        strm << static_cast< int >(lvl);
    return strm;
}

void init() {
    // logging::register_simple_formatter_factory< severity_level >("Severity");
    logging::add_file_log(
            keywords::file_name = "blop.log",
            keywords::auto_flush = true,
            keywords::open_mode = (std::ios::out | std::ios::app),
            keywords::format = "%TimeStamp% [%Uptime%] (%LineID%) <%Severity%>: %Message%"
            );
    logging::add_common_attributes();
    logging::core::get()->add_global_attribute("Uptime", attrs::timer());
}

int main(int argc, const char *argv[]) {
    init();
    src::severity_logger_mt< severity_level > lg = my_logger::get();
    BOOST_LOG_SEV(lg, DEBUG) << "A debug severity message";
    BOOST_LOG_SEV(lg, INFO) << "An informational severity message";
    BOOST_LOG_SEV(lg, WARNING) << "A warning severity message";
    BOOST_LOG_SEV(lg, ERROR) << "An error severity message";
    BOOST_LOG_SEV(lg, CRITICAL) << "A critical severity message";
    return 0;
}

Notice the commented-out line :

// logging::register_simple_formatter_factory< severity_level >("Severity");

which produces the mentioned error.

I compile it with:

g++ main.cpp -Wall -DBOOST_ALL_DYN_LINK  -lboost_system -lboost_log_setup -lboost_log -lboost_filesystem -lboost_date_time -lboost_thread -o main
Steeplejack answered 6/4, 2013 at 17:33 Comment(1)
Perhaps, this question is related: #18014835Rebeccarebecka
T
27

Ran into the same problem (blank %Severity% in the log file AND the same compiler error when trying to add the register function). I found that the call to register_simple_formatter_factory should be:

boost::log::register_simple_formatter_factory< boost::log::trivial::severity_level, char >("Severity");
Towpath answered 13/10, 2013 at 21:57 Comment(1)
You probably have to add to your answer that you have to do that before add_file_log (or similar) call, otherwise it doesn't work silently.Darden
P
3

You can make your own backend and call set_formatter on it. Here's my code showing how to do it plus a few additions like the log file cleanup and auto flush:

BOOST_LOG_DECLARE_GLOBAL_LOGGER(db_logger, src::severity_logger_mt< dreambridge::log::LogLevel >);

#define LOGGER(level) BOOST_LOG_STREAM_SEV(db_logger::get(), level)
    void InitializeFileLog(const std::string & logDir)
    {   
        boost::shared_ptr< logging::core > loggingCore = logging::core::get();

        db_logger::get().add_attribute(
            "TimeStamp",
            boost::make_shared< attrs::local_clock >());

        string logPath = logDir + "/DreamBridgeSapi2MrcpTts_%N.log";

        boost::shared_ptr< sinks::text_file_backend > backend =
            boost::make_shared< sinks::text_file_backend >(
                // file name pattern
                keywords::file_name = logPath,
                // rotate the file upon reaching 5 MiB size...
                keywords::rotation_size = 5 * 1024 * 1024,
                // ...or at midnight, whichever comes first
                keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0)                    
            );

        backend->auto_flush(true);

        // Wrap it into the frontend and register in the core.
        // The backend requires synchronization in the frontend.
        typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t;
        boost::shared_ptr< sink_t > sink(new sink_t(backend));

        loggingCore->add_sink(sink);

        backend->set_formatter(fmt::stream
                        << fmt::date_time< boost::posix_time::ptime >("TimeStamp")
                        << " : [" << fmt::attr< dreambridge::log::LogLevel >("Severity")
                        << "] " << fmt::message()
                        );

        backend->set_file_collector(sinks::file::make_collector(
            // rotated logs will be moved here
            keywords::target = logDir + "/old_logs",
            // oldest log files will be removed if the total size reaches 100 MiB...
            keywords::max_size = 100 * 1024 * 1024,
            // ...or the free space in the target directory comes down to 50 MiB
            keywords::min_free_space = 50 * 1024 * 1024
        ));

        try
        {
            backend->scan_for_files(sinks::file::scan_all);
        }
        catch(std::exception & e)
        {
            LOGGER(emergency) << "exception during scanning : " << e.what();

        }

    }

also you can set the logging level as:

void SetLogLevel(dreambridge::log::LogLevel logLevel)
    {
        boost::shared_ptr< logging::core > loggingCore = logging::core::get();

        loggingCore->set_filter
        (
            flt::attr< dreambridge::log::LogLevel >("Severity") <= logLevel
        );
    }

note: This code is from my own repository. dreamdridge is the product name, hence the namespaces.

Peptide answered 22/4, 2013 at 19:15 Comment(0)
N
3

Here's the prototype of register_simple_formatter_factory function template:

template<typename AttributeValueT, typename CharT>
void register_simple_formatter_factory(attribute_name const &);

That means you forgot typename CharT, so, here's the correct one:

logging::register_simple_formatter_factory<severity_level, char>("Severity");

Or some other character types, like wchar_t.

Noachian answered 14/6, 2014 at 15:24 Comment(0)
I
2

Correcting the call to register the formatter (i.e. operator<<) as pointed out by the answers from W. Merrt and Matt Yang is all you need to make your current code compile and run correctly.

However, in order to make full use of Boost Log's filtering capabilities you also need to define operator>> for your custom severity levels and then register that method with the filter factory.

template< typename CharT, typename TraitsT>
inline std::basic_istream< CharT, TraitsT >& operator>> (
    std::basic_istream< CharT, TraitsT >& strm, severity_level &lvl)
{
    std::string tmp;
    strm >> tmp;

    // can make it case insensitive to allow 'warning' instead of only 'WARNING'
    // std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper);

    // if you have a lot of levels you may want use a map instead
    if (tmp.compare("DEBUG") == 0) lvl = DEBUG;
    else if (tmp.compare("INFO") == 0) lvl = INFO;
    else if (tmp.compare("WARNING") == 0) lvl = WARNING;
    else if (tmp.compare("ERROR") == 0) lvl = ERROR;
    else if (tmp.compare("CRITICAL") == 0) lvl = CRITICAL;
    // provide a default value for invalid strings
    else lvl = DEBUG;
    // or you could throw an exception
    // else tmp.append(" is not a valid severity level."), throw std::invalid_argument(tmp);

    return strm;
}

void init() {
    // register operator<< for severity_level
    logging::register_simple_formatter_factory< severity_level, char >("Severity");
    // register operator>> for severity_level
    logging::register_simple_filter_factory< severity_level >("Severity");
    logging::add_file_log(
            keywords::file_name = "blop.log",
            keywords::auto_flush = true,
            keywords::open_mode = (std::ios::out | std::ios::app),
            keywords::format = "%TimeStamp% [%Uptime%] (%LineID%) <%Severity%>: %Message%",
            // now you can filter using the name of the severity level
            keywords::filter = "%Severity% >= INFO"                );
    logging::add_common_attributes();
    logging::core::get()->add_global_attribute("Uptime", attrs::timer());
}
Iaria answered 11/1, 2016 at 19:14 Comment(0)
T
1

The reason could be that you shouldn't write data to std::ostream. boost.log uses boost.log.formatting_ostream to output logs, not std::ostream. please see the this code extracted from page http://www.boost.org/doc/libs/1_54_0/libs/log/doc/html/log/detailed/expressions.html

// The operator is used when putting the severity level to log
logging::formatting_ostream& operator<<
(
    logging::formatting_ostream& strm,
    logging::to_log_manip< severity_level, severity_tag > const& manip
)
{
    static const char* strings[] =
    {
        "NORM",
        "NTFY",
        "WARN",
        "ERRR",
        "CRIT"
    };

    severity_level level = manip.get();
    if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
        strm << strings[level];
    else
        strm << static_cast< int >(level);

    return strm;
}
Taxiplane answered 19/7, 2013 at 6:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.