On 05.08.2015 16:30, Robert Dailey wrote:
I find the simplest things the most frustrating to accomplish in
Boost.Log. I know that some compilers have predefined preprocessor
macros to provide information. MSVC for example has __LINE__,
__FILE__, etc.
I need Boost.Log to print line, file, and function information for me
in a platform agnostic way. The documentation is not easy to navigate
through and find the information I need. Also examples on this are
sparse. I've seen some people calling BOOST_LOG_NAMED_SCOPE manually
prior to each log statement. This seems unintuitive.
How can I simply print this contextual information with each log line
in an automated way? Can't Boost.Log piggy-back on compiler features
available to it?
__LINE__ and __FILE__ are pretty much standard thing, not MSVC specific ...
As for the documentation, it is not so bad, it is just written like a
cookbook that requires a master chef degree to be able to follow the
recipes... :-) and is in some places a bit behind the actual
implementation. It took me couple of days of experimenting with the
library to get me where I wanted to be ...
There are several ways to achieve what you're after ... the path I've is
to add your own attributes to each log record (which you now can do even
after the record was opened and went through filters), and to define
sink formatters that will display them. In my final solution I have
replaced boost boilerplate log macros with my own which add required
attributes. For instance, using the severity channel logger:
typedef boost::log::sources::severity_channel_logger logger_t;
I defined the following macro:
#define MY_CONTEXT_LOG_(logger_, sctx_, severity_, message_) do { \
boost::log::record rec =
logger_.open_record(boost::log::keywords::severity =
my::log::severity::severity_ ); \
if (rec) \
{ \
rec.attribute_values().insert( \
boost::log::attribute_name("File"), \
boost::log::attributes::constantstd::string(boost::filesystem::path(__FILE__).filename().string()).get_value());
\
rec.attribute_values().insert( \
boost::log::attribute_name("Line"), \
boost::log::attributes::constant<unsigned
int>(__LINE__).get_value()); \
rec.attribute_values().insert( \
boost::log::attribute_name("Context"), \
boost::log::attributes::constantstd::string(sctx_).get_value()); \
boost::log::record_ostream strm(rec); \
strm << message_; \
strm.flush(); \
logger_.push_record(boost::move(rec)); \
} } while (false)
And the formatter for the stream sink:
#define STREAM_FORMAT expr::stream << \
expr::format_date_timeboost::posix_time::ptime("TimeStamp", "%Y-%m-%d
%H:%M:%S.%f") \
<< " [" << expr::attr<severity>("Severity") << "] " \
<< expr::attrstd::string("Channel") \
<< " " << expr::attrstd::string("Context") << " " \
<< "<" << expr::attrstd::string("File") \
<< ":" << expr::attr<unsigned int>("Line") << ">" \
<< " - " << expr::smessage
It is cumbersome but for now it fits my needs. The named scopes are fine
but it is my understanding that they are thread local whereas I need the
same log context to span across the threads. It would also be possible
to write own logger that would add the attributes internally but I
didn't (yet) bother with that.
Just as a note, in order to have a timestamp automatically appended,
register timestamp attribute to the core:
core->add_global_attribute("TimeStamp",
boost::log::attributes::utc_clock());
Also, the __FILE__ macro evaluates to the complete absolute file path -
which is usually too much and just a file name would suffice.
Hope it helps,
Leon