On Dec 22, 2004, at 4:33 PM, Christopher Sm ith wrote:

With the normal idiom for logstreams you would want different streams for each log level which would avoid this issue entirely.



What "normal" idiom? I'm not aware of any established patterns for using a STL stream-like interface for logging. If there are, references would be nice and we can add their ideas in the mix in addition to yours and mine.


What I was anticipating is that you would construct the logstream on entry to a method (or have it passed in as a reference) and use it for all log requests within the message body. Are you suggesting creating a logstream for each level that might be used within the method and appropriately initializing them with your favorite options for precision, width, etc? If you were doing that and wanted to pass references to logstreams to called methods, you would have to pass 2 or 3 logstream&'s and hope that you didn't want to add another level.


Regardless it would seem necessary to use something like iostate savers to ensure you didn't leave the stream in an unexpected state. For example, if you have two methods:

Foo::bar() {
        logstream << getCount() << LOG4CXX_ENDMSG;
        //.. stuff
}

Foo::baz() {
        boost::io::ios_flags_saver ifs(logstream);
        logstream << std::hex << getPointer() << LOG4CXX_ENDMSG;
        //.. stuff
}

Were you expecting to have logstream be an instance or static variable? The actions on a log stream are not transactional, if you tried to call Foo::bar() simultaneously with Foo::baz() (assuming that logstream was a instance variable) you could get any number of permutations like:


logstream << std::hex << getCount << getPointer() << LOG4CXX_ENDMSG << LOG4CXX_ENDMSG;

which would have two logging requests one with count and pointer and a second empty one.





Now, if you remove ifs from Foo::baz(), then the format of getCount() in Foo::bar() will depend on whether Foo::baz() was called previously, which is even worse than it being impacted by what log level you have things set at. Once you are using ifs consistently, applying manipulators regardless of log levels just becomes a waste of CPU cycles and a source of additional code branching.



If code sections did not want to share conversion settings, they should use separate logstream instances. Multiple logstreams can dispatch to the same logger.



Say I was entering a method that did an iterative calculation and wanted to consistently use a %e12.5 representation for floating point. If manipulators were skipped if the current level was not at the threshold, then I would have to do something like:


logstream logstream(log, Level::ALL);
logstream << std::scientific;
logstream.width(12);
logstream.precision(5);
logstream << Level::DEBUG;




In general, if you weren't using logstreams and were instead manually using ostreams, it'd be very odd to have your I/O manipulators behave as you are suggesting. Which seems more natural:

if (logger.isInfoEnabled()) {
logstream << "We have: " << getCount() << " floats at: " << std::hex << getPointer() << LOG4CXX_ENDMSG;
}


or:

if (logger.isInfoEnabled()) {
        logstream << "We have: " << getCount() << " floats at: ";
}

logstream << std::hex;

if (logger.isInfoEnabled()) {
        logstream << getPointer() << LOG4CXX_ENDMSG;
}

If I was doing:

filestream << "We have: " << std::scientific << 3.1415926 << std::endl;

At the end of the statement, I would expect that either an exception had been thrown (disk full, etc) or the filestream would have be left with the file stream using scientific notation.

If I had used an explicit if, then obviously it would be silly for me to make an assertion about the state of the filestream after the if block. Since the explicit statement doesn't look conditional, the state of the stream after the statement should be the same as if I was writing to a filestream.

My point was though, regardless of which implementation we go with, the I/O manipulators can be setup to behave as per either approach. Indeed, if we *really* wanted to give people a choice of in the matter we could simply use a policy class. Heck, in theory we could make the "inheritence-vs-delegation" approach work that way as well, although that might be just a tad too unwieldy.






Reply via email to