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