I am using this code snippet to create a Boost logger:
#include <boost/log/common.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/utility/empty_deleter.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
using namespace boost::log;
int main()
{
typedef sinks::asynchronous_sink<sinks::text_ostream_backend> text_sink;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
boost::shared_ptr<std::ostream> stream{&std::clog,
boost::empty_deleter{}};
sink->locked_backend()->add_stream(stream);
core::get()->add_sink(sink);
sources::logger lg;
BOOST_LOG(lg) << "note";
sink->flush();
}
I want to log it to a file. I found a solution for this explained here on SO. The name for the log file shall be read from the command line (the class is handling the command line is based on Boost program_options and works). I am also using a try/catch block to capture errors. My main()
now looks like this:
int main(int argc, char* argv[])
{
try
{
CConfig cConfig(argc, argv[]);
// Get the rdbuf of clog.
// We need it to reset the value before exiting.
auto old_rdbuf = std::clog.rdbuf();
if(cConfig.hasLogfile())
{
std::ofstream out(cConfig.getLogfile());
// Set the rdbuf of clog.
std::clog.rdbuf(out.rdbuf());
}
typedef sinks::asynchronous_sink<sinks::text_ostream_backend> text_sink;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
boost::shared_ptr<std::ostream> stream{&std::clog,
boost::empty_deleter{}};
sink->locked_backend()->add_stream(stream);
core::get()->add_sink(sink);
sources::logger lg;
BOOST_LOG(lg) << "note";
sink->flush();
if(cConfig.hasLogfile())
{
// Reset the rdbuf of clog.
std::clog.rdbuf(old_rdbuf);
}
}
catch (std::exception& cException)
{
std::cerr << "Error: " << cException.what() << std::endl;
return 71;
}
catch (...)
{
std::cerr << "A fatal exception error has occurred." << std::endl;
return 71;
}
return 0;
}
Now, for good object-oriented style, I want to extract the code into its own class, so that only the following should remain on my main.cpp
. The logger should also log the errors:
int main(int argc, char* argv[])
{
CLogger cLogger();
try
{
CConfig cConfig(argc, argv);
cLogger.init(cConfig);
cLogger.log("note");
}
catch (std::exception& cException)
{
std::cerr << "Error: " << cException.what() << std::endl;
cLogger.log(cException.what());
return 71;
}
catch (...)
{
std::cerr << "A fatal exception error has occurred." << std::endl;
cLogger.log("A fatal exception error has occurred.");
return 71;
}
cLogger.finish();
return 0;
}
My problem is how to pass the variables sink
and old_rdbuf
from init()
to finish()
? In Java I would just pass them as class member as follows:
// logger.h
#include …
using namespace boost::log;
typedef sinks::asynchronous_sink<sinks::text_ostream_backend> text_sink;
class CLogger { … }
// logger.cpp
// Declare (private) class member variables:
boost::shared_ptr<text_sink> sink;
auto old_rdbuf;
void CLogger::init(CConfig &cConfig)
{
if (cConfig.hasLogfile())
{
std::ofstream out(cConfig.getLogfile());
// Get the rdbuf of clog.
// We need it to reset the value before exiting.
old_rdbuf = std::clog.rdbuf();
// Set the rdbuf of clog.
std::clog.rdbuf(out.rdbuf());
}
sink = boost::make_shared<text_sink>();
boost::shared_ptr<std::ostream> stream{&std::clog,
boost::empty_deleter{}};
sink->locked_backend()->add_stream(stream);
core::get()->add_sink(sink);
}
void CLogger::log(char* text)
{
sources::logger lg;
BOOST_LOG(lg) << text;
}
void CLogger::finish()
{
sink->flush();
if(old_rdbuf != null)
{
std::clog.rdbuf(old_rdbuf);
}
}
As far as I understand it, this will not work, because sink is an object which will be destroyed when the body block of init()
is closed, and the member variable sink
then will then point somewhere into memory where the object isn’t anymore (an invalid pointer, but not null
). Am I right? Second, the compiler does not accept my member declaration of old_rdbuf
. How do I pass the two correctly to finish()
?
Side question, is there a more direct way to pass the file name (std::string CConfig.getLogfile()
) to the logger (as an ostream)? I tried
boost::shared_ptr<std::ostream> stream{cConfig.getLogfile(), boost::empty_deleter{}};
and
boost::shared_ptr<std::ostream> stream{&cConfig.getLogfile(), boost::empty_deleter{}};
but that does not work at all. One more question: Why are the constructors for stream
and empty_deleter
written with braces, I would have expected parentheses?