I don't believe that your current method will work in all cases, especially if your code is threaded. It made me nervous when you said that a single logged format flag worked to fix multiple loggers, so I looked at the code (record_ostream.hpp and record_ostream.cpp).
Boost Log uses the Object pool design pattern to provide stream formatters to loggers with the auxiliary struct stream_provider
. The implementation of stream_provider
uses thread-local storage (when threading is supported) to use a separate pool of ostream
instances for each thread. Within a pool, ostream
instances are created as needed - if only one formatter is needed at a time, only one will ever be created. So I think your current workaround will work for the current Boost Log implementation if you log from a single thread and if you never log something in the middle of logging something else.
How this fails with threading should be pretty obvious. Here's a simple example of how this can fail in a single thread:
static double f(double x) {
BOOST_LOG(my_logger::get()) << "called f with " << x;
return x;
}
int main() {
BOOST_LOG(my_logger::get()) << std::scientific << "format flag";
BOOST_LOG(my_logger::get()) << "top level " << f(0.01);
return 0;
}
which produces:
[2014-04-27 14:16:39.832008] [0x000007fff7a1c631] [info] format flag
[2014-04-27 14:16:39.832616] [0x000007fff7a1c631] [info] called f with 0.01
[2014-04-27 14:16:39.832630] [0x000007fff7a1c631] [info] top level 1.000000e-02
Note that the top level log entry (on the third line) is formatted properly while the function log entry (on the second line) is not. This is because the configured stream was in use when the function was called so a separate stream was created and used.
I think @WilliamKunkel's suggestion to define your own logging macro(s) is the best way I've seen to handle this. If you really want to use the Boost macros, however, this hack worked for me on Boost 1.55:
#include <iomanip>
#define BOOST_LOG_DYN_LINK
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
typedef boost::log::sources::logger logger_t;
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, logger_t)
namespace boost {
namespace log {
namespace aux {
template<>
BOOST_FORCEINLINE record_pump<logger_t> make_record_pump(logger_t& lg, record& rec)
{
auto rp = record_pump<logger_t>(lg, rec);
rp.stream() << std::scientific;
return rp;
}
}
}
}
int main() {
BOOST_LOG(my_logger::get()) << "Logging number " << 0.01;
return 0;
}
The basic idea is to specialize the template function that supplies the stream to the macro. When it is specialized to the actual logger class you are using, the specialized implementation passing the std::scientific
flag will be preferred to the generic implementation.
This is a hack because it depends on implementation details of Boost Log and is not guaranteed to work from release to release. Defining your own macro seems to me like a much better way to go.
I had hoped that something could be done with boost::log::basic_formatting_stream
because its header says:
Although basic_formatting_ostream
does not derive from std::basic_ostream
, users are not required to add
special overloads of operator<<
for it since the stream will by default reuse the operators for std::basic_ostream
.
However, one can define special overloads of operator<<
for basic_formatting_ostream
if a certain type needs
special formatting when output to log.
Unfortunately, it looks like this only applies to class types and not primitive types as there are member function implementations of operator<<
for all primitive types.