On Oct 4, 2004, at 7:50 PM, Christopher Smith wrote:
Curt Arnold wrote:Per the previous discussion on iostream-free log4cxx, this alternative API should be completely optional. Ideally included in its own header (log4cxx/loggingstream.h?) and delegating to the primary API.
Yup, I set it up entirely in a header.
A couple of issues:
1. It would be good that logging streams weren't limited to one character type, so having a template like:
template<class Elem, class Tr = char_traits<Elem>> class log4cxx::basic_logging_stream : std::basic_ostream<Elem, Tr> {
}
would be good, though there might be a log4cxx::stream and log4cxx::wstream expansions of that template.
Great minds think alike. ;-)
2. The LOG4CXX_.* macros provide context values (__FILE__ and __LINE__ currently, may be expanded to include __func__). How would you see adding that information into the logging stream?
I used a macro for instantiating the stream. It passes in the file and line number at the point of instantiation. So, the current model is you would use a new stream instance for each log event you generate.
See 4.
3. What would trigger the creation of a logging event? My initial take is that it should be a call to flush() or close().
I made an enum with it's own endl equivalent. I'm trying to figure out how to use the standard iostream endl, but have hit a snag there.
std::endl is defined to call flush(), so constructing the message on flush seems appropriate,
4. Would a logging stream be able to create multiple logging events?
Right now no. However, I could restructure things so the "endl" is a macro, which passes in __FILE__ and __LINE__. Then I could make the flush also reset the stream and you could do multiple logging events. I don't expect much of a win from this, and I haven't come up with a way to do it which wouldn't add a bit of overhead.
If flush (not close) triggers a logging event, the a logging stream should be able to create multiple logging events or code that worked fine on normal std::ostream's would not work on log4cxx::stream. I don't like redefining endl which would result in possible confusion when std::endl is used in some contexts and log4cxx::endl in others.
Inserting a LevelPtr seems a pretty intuitive way to change the level of a logging stream. That is
logstream << log4cxx::Level::INFO << "This is an info msg" << endl;
log4j has a LocationInfo class that doesn't have an analog in log4cxx (but probably should). If we define a core LocationInfo class, then the insertion of a LocationInfo could change the reported file and line. A define like
#define LOG4CXX_ENDL log4cxx::LocationInfo(__FILE, __LINE__) << std::endl
would allow usage like:
logstream << "This is a msg " << LOG4CXX_ENDL; logstream << "This is another msg " << LOG4CXX_ENDL; logstream.close();
5. When would the level be checked to determine if the stream is enabled. My take would be on stream creation and after calls to flush. If it were checked on each insertion operation, messages could look pretty funky if the configuration was modified while a message was being assembled.
Right now I'm doing it at stream creation. I store it in a const member variable, which allows the compiler to optimize all the checks for each operator<< into a single check. If I did multiple events per stream, I should think I'd probably have to do a check on each flush. Perhaps not though. Maybe each instantion would correspond to a check, and reuse of the stream would imply that dong a new check is not necessary.
There are probably cases where you'd want to check at each flush and other cases where you'd like to bypass that overhead. Maybe the behavior could be to only check on creation and any insertion of log4cxx::LevelPtr, so if you really wanted the level to be reevaluated you'd just insert the level again.
6. Would the level of a logging stream be able to be modified? It might be convenient, however you would not be able to change the level mid-way through construction of a message since earlier parts might have been no-op'd. My take is that inserting a LevelPtr would imply a flush.
I chose to go with the level being locked. Given that creating a LoggingStream is cheap, and keeping things fixed allows the compiler to make better optimization choices, I think this is the right way to go.
Fixing the level of the stream might discourage changing the level of a message, especially if the logging stream was passed as a parameter.
