Boost logo

Boost Users :

Subject: Re: [Boost-users] Inability to print LINE, FILE, FUNCTION in Boost.Log?
From: Leon Mlakar (leon_at_[hidden])
Date: 2015-08-05 12:38:46


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<my::log::severity,
std::string> 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::constant<std::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::constant<std::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_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d
%H:%M:%S.%f") \
   << " [" << expr::attr<severity>("Severity") << "] " \
   << expr::attr<std::string>("Channel") \
   << " " << expr::attr<std::string>("Context") << " " \
   << "<" << expr::attr<std::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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net