Menu

Boost Global Logger How to set severity

pdlarue
2015-04-24
2015-04-27
  • pdlarue

    pdlarue - 2015-04-24

    Oops! This really should be in the Help Forum. Sorry for spamming by mistake!!

    I've been trying for days now to create a Boost Global Logger for use throughout the application but I can't seem to get the severity level set in the Global Logger.

    Right out of the Boost Documentation here

    ...it would be more convenient to have one or several global loggers in
    order to easily access them in every place when needed. In this regard
    std::cout is a good example of such a logger.

    The library provides a way to declare global loggers that can be
    accessed pretty much like std::cout. In fact, this feature can be used
    with any logger, including user-defined ones. Having declared a global
    logger, one can be sure to have a thread-safe access to this logger
    instance from any place of the application code. The library also
    guarantees that a global logger instance will be unique even across
    module boundaries. This allows employing logging even in header-only
    components that may get compiled into different modules.


    Regardless of the macro you used to declare the logger, you can
    acquire the logger instance with the static get function of the logger
    tag:

    src::severity_logger_mt< >& lg = my_logger::get();
    

    Now, I'm trying a simpletest. Here is the code:

    HEADER

    #ifndef ONEPRINT_LOGGER_H
    #define ONEPRINT_LOGGER_H
    
    #include <boost/log/core/core.hpp>
    #include <boost/log/attributes/attribute_value_set.hpp>
    #include <boost/log/trivial.hpp>
    #include <boost/log/sources/severity_logger.hpp>
    #include <boost/log/utility/setup/file.hpp>
    #include <boost/log/utility/setup/console.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/log/sinks.hpp>
    #include <boost/core/null_deleter.hpp>
    #include <iostream>
    
    namespace expr = boost::log::expressions;
    namespace sources = boost::log::sources;
    namespace sinks = boost::log::sinks;
    namespace keywords = boost::log::keywords;
    
    namespace logs {
    
        enum severity_level
        {
            normal,
            warning,
            error,
            critical
        };
    
        class Logger {
        public:
            Logger();
            ~Logger();
            void log(int sev, std::string msg);
    
        protected:
            typedef sinks::asynchronous_sink<sinks::text_ostream_backend> asynchronousSink;
            boost::shared_ptr<Logger::asynchronousSink> warningSink;
            boost::shared_ptr<boost::log::core > core;
            boost::shared_ptr<sinks::text_ostream_backend> backend;
    
            boost::shared_ptr<asynchronousSink> initAsynchronousSink();
            void addSinks();
    
            //boost::shared_ptr<std::ostream> getStream();
    
        };
    
    }
    #endif //ONEPRINT_LOGGER_H
    

    CPP

    #include "Logger.h"
    
    class counter;
    
    using namespace logs;
    
    namespace {  // NON-MEMBER METHODS
        bool onlyWarnings(const boost::log::attribute_value_set& set)
        {
            return set["Severity"].extract<int>() > 0;
        }
    
        void severity_and_message(const boost::log::record_view &view, boost::log::formatting_ostream &os)
        {
            os << view.attribute_values()["Severity"].extract<int>() << ": " <<
            view.attribute_values()["Message"].extract<std::string>();
        }
    }
    
    Logger::Logger() {
        core = boost::log::core::get();
        backend = boost::make_shared<sinks::text_ostream_backend>();
        addSinks();
    }
    
    Logger::~Logger() {
    
    }
    
    /**
     * Creates asynchronous sinks.  Can be called multiple times to create more than one
     */
    boost::shared_ptr<Logger::asynchronousSink> Logger::initAsynchronousSink() {
        //frontend sinks are instantiated with shared_ptr
        typedef sinks::asynchronous_sink<sinks::text_ostream_backend> text_sink;
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    
        //create and add ostream to this sink
        boost::shared_ptr<std::ostream> stream{&std::clog, boost::null_deleter{}};
        sink->locked_backend()->add_stream(stream);
        return sink;
    }
    
    /**
     * Setup all sinks being used and add them to the core
     */
    void Logger::addSinks() {
        warningSink = initAsynchronousSink();
        warningSink->set_filter(&onlyWarnings);
        warningSink->set_formatter(&severity_and_message);
        core.get()->add_sink(warningSink);
    }
    
    void Logger::log(int sev, std::string msg) {
        sources::severity_logger<int> severityLogger;
        BOOST_LOG_SEV(severityLogger, sev) << msg;
        warningSink->flush();
    }
    

    loggertest.cpp

    #include "Logger.h"
    
    using namespace logs;
    
    int main(int argc, char* argv[]) {
        BOOST_LOG_SEV(lg::get(), severity_level::normal) << "note";
        BOOST_LOG_SEV(lg::get(), severity_level::warning) << "warning";
        BOOST_LOG_SEV(lg::get(), severity_level::critical) << "critical";
    
        return 0;
    }
    

    I found a better example 62.10. A macro to define a global logger and a working version in this SO question. But the working example does not use the get() method. So, after declaring the BOOST_LOG_GLOBAL_LOGGER, I was able to access log::get() but I still can't get it to recognize the severity.

    Here is the output:

    /home/pdl/.clion/system/cmake/generated/32331b87/32331b87/Debug/BoostTest
    [2015-04-17 18:55:06.821943] [0x000007f85ad20278] [info]    note:
    [2015-04-17 18:55:06.822470] [0x000007f85ad20278] [info]    warning:
    [2015-04-17 18:55:06.822484] [0x000007f85ad20278] [info]    critical:
    
    Process finished with exit code 0
    

    Even though I've specified BOOST_LOG_SEV(lg, CRITICAL) everything gets logged as [info]

    Will someone please help me figure out what is missing? Thank you in advance.

     

    Last edit: pdlarue 2015-04-24
  • Andrey Semashev

    Andrey Semashev - 2015-04-24

    The problem is the severity type mismatch.

    You have instantiated severity_logger_mt with default template parameters, so the severity level attribute has type int. Your enum values are converted to int and sent to the logging core. You have not set up any sinks, so by default the default sink is used. The sink attempts to extract severity level attribute value from log records, but fails to do that because it expects the severity level to be of type boost::log::trivial::severity_level. After that failure the sink falls back to boost::log::trivial::severity_level::info severity.

    If you want to use your enum for severity levels you have to (a) specify it in the logger template parameters and (b) set up a sink with a formatter that is aware of your enum.

     

    Last edit: Andrey Semashev 2015-04-24
  • pdlarue

    pdlarue - 2015-04-24

    Andrey:

    First, thank you for the tip. Do you mean using:
    BOOST_LOG_WITH_PARAMS(logger, params_seq)

    I can't find any examples where this is used. I have no idea how to specify the severity levels in the logger template parameters. Can you give me an example?

     
  • Andrey Semashev

    Andrey Semashev - 2015-04-24

    See here about the logger, you need to write src::severity_logger_mt< severity_level >. Here is something about setting up sinks and formatters.

     
  • pdlarue

    pdlarue - 2015-04-24

    Andrey:

    Again, thank you so much for your help. I've updated the code to show my non-global logger code. You can see that I am creating a sink and using a formatter. However, in the header file, I did add the global logger. So, now when I run the loggertest.cpp, it is using the global logger and my output is still using the default severity (as you said) in your first post. I've been all over this documentation and I'm just not getting it.

    If you want to use your enum for severity levels you have to
    (a) specify it in the logger template parameters and
    (b) set up a sink with a formatter that is aware of your enum.

    I'll deal with the sink formatter once I can specify severity levels in the logger template parameters. I'm stuck.

    How do I specify it in the logger template parameters? What template should I use? What is the syntax?

     
  • pdlarue

    pdlarue - 2015-04-24

    Maybe, I stumbled across something like what you are talking about. Look here:

    sink1->set_formatter(
            expr::format("[%1%]<%2%>(%3%): %4%") % expr::format_date_time < boost::posix_time::ptime
                    > ("TimeStamp", "%Y-%m-%d %H:%M:%S") % expr::attr < sign_severity_level > ("Severity") % expr::attr
                    < attrs::current_thread_id::value_type > ("ThreadID") % expr::smessage);
        sink1->set_filter(expr::attr < sign_severity_level > ("Severity") >= warning);
        logging::core::get()->add_sink(sink1);
    
        logging::add_common_attributes();
    }
    BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger_mt<sign_severity_level>)
    
     
  • Andrey Semashev

    Andrey Semashev - 2015-04-24

    The logger is the severity_logger_mt class template. You specify template parameters in angle brackets. The first (and only) template parameter is the type of severity level, so you have to write severity_logger_mt< severity_level > when you need to specify the logger type. When you declare a global logger you use that type as a parameter to the global logger definition macro, like BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT.

     
  • pdlarue

    pdlarue - 2015-04-26

    Andrey:

    I tried a different approach and I'll post the code here:

    HEADER

    #include <boost/log/trivial.hpp>
    #include <boost/log/sources/global_logger_storage.hpp>
    
    enum severity_level
    {
        normal,
        warning,
        error,
        critical
    };
    
    BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt< severity_level >)
    

    CPP

    #include "GlobalLogger.h"
    
    #include <boost/log/expressions/formatters/date_time.hpp>
    #include <boost/log/expressions.hpp>
    #include <boost/log/sinks/sync_frontend.hpp>
    #include <boost/log/sinks/text_ostream_backend.hpp>
    #include <boost/log/support/date_time.hpp>
    #include <boost/core/null_deleter.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/log/utility/setup/file.hpp>
    #include <boost/log/utility/setup/console.hpp>
    #include <boost/log/sinks.hpp>
    #include <fstream>
    
    namespace logging = boost::log;
    namespace src = boost::log::sources;
    namespace expr = boost::log::expressions;
    namespace sinks = boost::log::sinks;
    namespace attrs = boost::log::attributes;
    
    bool onlyWarnings(const boost::log::attribute_value_set& set)
    {
        return set["Severity"].extract<int>() > 0;
    }
    
    void severity_and_message(const boost::log::record_view &view, boost::log::formatting_ostream &os)
    {
        os << view.attribute_values()["Severity"].extract<int>() << ": " <<
        view.attribute_values()["Message"].extract<std::string>();
    }
    
    BOOST_LOG_GLOBAL_LOGGER_INIT(logger, boost::log::sources::severity_logger_mt< severity_level >)
    {
        boost::log::sources::severity_logger_mt< severity_level > logger;
    
        // add a text sink
        typedef sinks::asynchronous_sink<sinks::text_ostream_backend> text_sink;
        boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    
        // add "console" output stream to our sink
        boost::shared_ptr<std::ostream> stream{&std::clog, boost::null_deleter{}};
        sink->locked_backend()->add_stream(stream);
    
        // specify the format of the log message
        sink->set_formatter(&severity_and_message);
    
        // just log messages with severity >= SEVERITY_THRESHOLD are written
        sink->set_filter(&onlyWarnings);
    
        // "register" our sink
        logging::core::get()->add_sink(sink);
    
        logging::add_common_attributes();
    
        return logger;
    }
    

    main.cpp

    #include <iostream>
    
    #include "GlobalLogger.h"
    
    using namespace std;
    
    int main() {
        boost::log::sources::severity_logger_mt<  severity_level >& lg = logger::get();
        BOOST_LOG_SEV(lg, severity_level::normal) << "note";
        BOOST_LOG_SEV(lg, severity_level::warning) << "warning";
        BOOST_LOG_SEV(lg, severity_level::critical) << "critical";
    
        return 0;
    }
    

    I must somehow still be using the default sink that falls back to boost::log::trivial::severity_level::info severity.

    What am I doing wrong? I'm just not getting it. Sorry for being so dumb. Thank you in advance for your help.

     
  • pdlarue

    pdlarue - 2015-04-27

    Hey, Andrey:

    Do I have to use an "INLINE" global logger definition macro (BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT)? I found one example where they used a C++ init method and put the macro name after the end of the method. Please forgive me, as my C++ isn't that great either. Thank you again.

     
  • Andrey Semashev

    Andrey Semashev - 2015-04-27

    I must somehow still be using the default sink

    Your code sets up the sink when the global logger is first created but your main function never uses the global logger and therefore it is never initialized. See the docs I referenced earlier, to obtain (and on first call - initialize) the global logger you have to write

    boost::log::sources::severity_logger_mt< severity_level >& lg = logger::get();
    

    where logger is the tag you passed to the BOOST_LOG_GLOBAL_LOGGER_INIT macro.

    Do I have to use an "INLINE" global logger definition macro (BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT)?

    You don't have to. You can use INLINE macros if you want to declare and define the logger in one place (typically, a header). Since logger initialization, like in your case, can introduce dependencies that are not needed to merely use the logger, it may be desirable to separate logger declaration and definition/initialization and put the latter part in a separate cpp file. That's what non-INLINE macros are for.

     
  • pdlarue

    pdlarue - 2015-04-27

    Andrey! YOU ARE GREAT!!!! I finally got it working! YAY!!! :-)

    Thanks for all your hard work in putting together samples and documentation but I couldn't have figured it out without your explanations here in this discussion. You're the best!!

    BTW: I didn't mean to spam both Help and Discussions but I don't know how to delete the one in Help. You can if you can. :-)

     

Log in to post a comment.