How do I set std::ios_base flags like std::left in boost log 2.0?

boost, boost-log, c++, logging

Solution

@rhashimoto makes a good point about how your current solution will break down with multiple threads/concurrent logging operations. I feel the best solution is to define your own logging macros to replace `BOOST_LOG_SEV` which have the stream modifiers included, like this:

#define LOG_SCIENTIFIC(logger, sev) (BOOST_LOG_SEV(logger, sev) << std::scientific)

Which can just be used as a drop-in replacement for `BOOST_LOG_SEV` which formats numbers as scientific. However, it's probably a pain to go through your code and replace every logging operation with a new custom macro. Instead of defining your own macro, you can also redefine `BOOST_LOG_SEV` to behave how you want. `boost/log/sources/severity_feature.hpp` defines `BOOST_LOG_SEV` as follows:

//! An equivalent to BOOST_LOG_STREAM_SEV(logger, lvl)
#define BOOST_LOG_SEV(logger, lvl) BOOST_LOG_STREAM_SEV(logger, lvl)

Because `BOOST_LOG_STREAM_SEV` is still a part of the public boost API, you ought to be able to safely redefine `BOOST_LOG_SEV` like this:

#define BOOST_LOG_SEV(logger, lvl) (BOOST_LOG_STREAM_SEV(logger, lvl) << std::scientific)

So long as this is defined after you have included the boost log headers, it should do what you want. However, I would recommend using a macro with a custom name, so that it is clear to others what your code is doing.

Problem

I have an application which makes extensive use of boost log 2.0. Now I would like to set some default flags for that application like `std::setprecision(std::numeric_limits<double>::digits10 + 1)`, `std::scientific` and `std::left`. But how do I do that? One approach is creating a logger at the very beginning of my main function and creating a dummy log message. This permanently sets the desired flags. But is there no nicer way to do this? edit in reply to: "OP should show actual code." I have a global Logging singleton, called L: ``` class L{ public: enum severity_level { dddebug, ddebug, debug, control, iiinfo, iinfo, info, result, warning, error, critical }; typedef boost::log::sources::severity_channel_logger< severity_level, // the type of the severity level std::string // the type of the channel name > logger_t; typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > text_sink; boost::shared_ptr< text_sink > sink_; static L& get(); static boost::shared_ptr<text_sink> sink(); static double t0(); static double tElapsed(); private: L(); double t0_p; static std::string tElapsedFormat(); L(const L&) = delete; void operator=(const L&) = delete; }; ``` which provides a logging sink, severity levels and utilizes MPI methods for synchronized timekeeping across MPI nodes. THe implementation of the class members follows here: ``` #include "log.h" #include <iomanip> #include <limits> #include <fstream> #include <boost/log/attributes/function.hpp> #include <boost/smart_ptr/shared_ptr.hpp> #include <boost/smart_ptr/make_shared_object.hpp> #include <boost/log/core.hpp> #include <boost/log/expressions.hpp> #include <boost/log/sources/severity_channel_logger.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #include <boost/log/utility/setup/common_attributes.hpp> namespace logging = boost::log; namespace src = boost::log::sources; namespace expr = boost::log::expressions; namespace sinks = boost::log::sinks; namespace attrs = boost::log::attributes; namespace keywords = boost::log::keywords; #include "mpiwrap.h" #include <mpi.h> BOOST_LOG_ATTRIBUTE_KEYWORD(t, "Time", std::string) BOOST_LOG_ATTRIBUTE_KEYWORD(rank, "Rank", int) BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", L::severity_level) L::L(): sink_(boost::make_shared< text_sink >()), t0_p(MPI_Wtime()) { sink_->locked_backend()->add_stream( boost::make_shared< std::ofstream >("log." + std::to_string(MpiWrap::getRank()))); sink_->set_formatter ( expr::stream << "< " << t << " " << "[p:" << rank << "] " << "[c:" << channel << "] " << "[s:" << severity << "] " << expr::smessage ); logging::core::get()->add_sink(sink_); logging::core::get()->set_filter( (channel == "ChannelName1" && severity >= dddebug) || (channel == "ChannelName2" && severity >= info) || (channel == "ChannelName3" && severity >= result) ); // Add attributes logging::core::get()->add_global_attribute("Time", attrs::make_function(&tElapsedFormat)); logging::core::get()->add_global_attribute("Rank", attrs::constant<int>(MpiWrap::getRank())); } L& L::get(){ static L instance; return instance; } boost::shared_ptr<L::text_sink> L::sink(){ return get().sink_; } double L::t0(){ return get().t0_p; } double L::tElapsed(){ return MPI_Wtime() - t0(); } std::string L::tElapsedFormat(){ std::stringstream ss; const int prec = std::numeric_limits<double>::digits10; ss << std::setw(prec + 2 + 6) << std::left << std::setprecision(prec) << tElapsed(); return ss.str(); } std::ostream& operator<< (std::ostream& strm, L::severity_level level) { static const char* strings[] = { "DBG3", "DBG2", "DBG1", "CTRL", "INF3", "INF2", "INF1", "RSLT", "WARN", "ERRR", "CRIT" }; if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings)) strm << strings[level]; else strm << static_cast< int >(level); return strm; } ``` Now for the usage: My classes usually have a static `logger_t` (typedef for `boost::log::sources::severity_channel_logger<severity_level, std::string>`) member ``` class A { public: logger_t logger; //other stuff here void function_which_does_logging(); }; L::logger_t A::logger(boost::log::keywords::channel = "ClassA"); void A::function_which_does_logging(){ //do non logging related stuff BOOST_LOG_SEV(logger, L::result) << "the error is: " << 0.1234567890; //do non logging related stuff } ``` My current solution to the problem is putting a logging statement in the beginning of my program ``` int main(){ L::logger_t logger(boost::log::keywords::channel = "init"); BOOST_LOG_SEV(logger, L::critical) << "setting up logger" << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1); //do stuff } ```

Original source