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.
...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:
#include"Logger.h"classcounter;usingnamespacelogs;namespace{// NON-MEMBER METHODSboolonlyWarnings(constboost::log::attribute_value_set&set){returnset["Severity"].extract<int>()>0;}voidseverity_and_message(constboost::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_ptrtypedefsinks::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 sinkboost::shared_ptr<std::ostream>stream{&std::clog,boost::null_deleter{}};sink->locked_backend()->add_stream(stream);returnsink;}/** * Setup all sinks being used and add them to the core */voidLogger::addSinks() {warningSink=initAsynchronousSink();warningSink->set_filter(&onlyWarnings);warningSink->set_formatter(&severity_and_message);core.get()->add_sink(warningSink);}voidLogger::log(intsev,std::stringmsg){sources::severity_logger<int>severityLogger;BOOST_LOG_SEV(severityLogger,sev)<<msg;warningSink->flush();}
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.
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
#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>namespacelogging=boost::log;namespacesrc=boost::log::sources;namespaceexpr=boost::log::expressions;namespacesinks=boost::log::sinks;namespaceattrs=boost::log::attributes;boolonlyWarnings(constboost::log::attribute_value_set&set){returnset["Severity"].extract<int>()>0;}voidseverity_and_message(constboost::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 sinktypedefsinks::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 sinkboost::shared_ptr<std::ostream>stream{&std::clog,boost::null_deleter{}};sink->locked_backend()->add_stream(stream);// specify the format of the log messagesink->set_formatter(&severity_and_message);// just log messages with severity >= SEVERITY_THRESHOLD are writtensink->set_filter(&onlyWarnings);// "register" our sinklogging::core::get()->add_sink(sink);logging::add_common_attributes();returnlogger;}
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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. :-)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
Now, I'm trying a simpletest. Here is the code:
HEADER
CPP
loggertest.cpp
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:
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
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
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?
See here about the logger, you need to write
src::severity_logger_mt< severity_level >
. Here is something about setting up sinks and formatters.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.
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?
Maybe, I stumbled across something like what you are talking about. Look here:
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 writeseverity_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.Andrey:
I tried a different approach and I'll post the code here:
HEADER
CPP
main.cpp
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.
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.
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 writewhere
logger
is the tag you passed to theBOOST_LOG_GLOBAL_LOGGER_INIT
macro.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.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. :-)