Friday 21 June 2013

Adapting to Boost Log

I've decided to see how Boost Log implements its non-evaluation magic. And, as far as I can see, it turns out to be clever, but it's not magic.

I've built Boost 1.54.0 beta1, and changed the trivial logging example to this:

int GetCode()
{
    static int val = 0;

    return ++val;
}

int main(int argc, char *argv[])
{
    boost::log::core::get()->set_filter(
        boost::log::trivial::severity >= boost::log::trivial::info);
 

    BOOST_LOG_TRIVIAL(trace) << "trace message: " << GetCode();
    BOOST_LOG_TRIVIAL(debug) << "debug message: " << GetCode();
    BOOST_LOG_TRIVIAL(info) << "info message: " << GetCode();
    BOOST_LOG_TRIVIAL(warning) << "warning message: " << GetCode();
    BOOST_LOG_TRIVIAL(error) << "error message:" << GetCode();
    BOOST_LOG_TRIVIAL(fatal) << "fatal message: " << GetCode();

    return 0;
}


And this was the output:

[2013-06-15 20:26:00.401384] [0x000010ec] [info]    info message: 1
[2013-06-15 20:26:00.411384] [0x000010ec] [warning] warning message: 2
[2013-06-15 20:26:00.411384] [0x000010ec] [error]   error message:3
[2013-06-15 20:26:00.421384] [0x000010ec] [fatal]   fatal message: 4


So, this means that the calls to GetCode()in these lines didn't run:

BOOST_LOG_TRIVIAL(trace) << "trace message: " << GetCode();
BOOST_LOG_TRIVIAL(debug) << "debug message: " << GetCode();


Looking at the preprocessor output, this is what we have (edited for readability):

for 
(
    BLR _boost_log_record_23 =  
        (BLT::logger::get()).open_record((BLK::severity = BLT::trace)); 
    !!_boost_log_record_23;
)
    BLA::make_record_pump((BLT::logger::get()), _boost_log_record_23).stream()
        << "trace message: " << GetCode();


where
BLR = ::boost::log::record
BLT = ::boost::log::trivial
BLK = ::boost::log::keywords
BLA = ::boost::log::aux

It uses a for so it can initialize the record and test the initialization in the same expression. That's where the filter is applied (I didn't know the !!, but from what I've read, it's a safe way of converting to bool), and if the record fails the filter, the body of the for is not executed.

Comparing Boost and Poco loggers, I like Boost's stream syntax better. I find it more convenient, since it frees the client app from having to worry about string formatting/concatenation. OTOH, I look at Poco's implementation, and not only can I understand most of it, but I can also reason about the bits I find more challenging. Not so much with Boost, so far.

I also prefer Poco's organization - e.g., all the convenience macros are in Poco/Logger.h. The idea I got from Boost (and I'm still checking this) is that there is no simplified way of getting a list of what's available (and judging from Qt Creator's auto-complete list, there is a lot available).

I won't give up my goal of being able to change logger implementations with little effort, but I may have to give up the goal of not burdening the client code with my code's requirements, if using Boost Log. I'm still trying to figure out if this will pose a problem, and, if so, how to solve it.

One thing is certain - I'll keep on using macros. I won't give up __FILE__ and __LINE__, and I certainly don't want to have to write both all over the place, so I need actual textual replacement, and that's something only the preprocessor can give me.

Next stop: Getting a better understanding of Boost Log.

No comments:

Post a Comment