|
Boost : |
From: Jamie Allsop (ja11sop_at_[hidden])
Date: 2008-02-17 19:07:57
Before I give my review I want to point out that I have been using
John's first logging library (with several key modifications) in
production for a couple of years now. On the whole it has been quite a
good library. Apart from allowing definition of logs in header files
only I did not have too much difficulty massaging it to fit my needs.
My approach to looking at this newer version of the library was to see
if I could use it in a way that is similar to how I have used the first
version. In addition I also wanted to see if it was possible to move
past limitations I encountered in the first revision.
Therefore with this review I'll point out what I tried to do with the
library to put into context my later comments on it.
Now for me I see logging as a very important library. There are plenty
of options out there and many of us will have written our own at some
point or other. With that in mind I feel that a logging library in boost
is simply not about providing a library that allows logging. It is about
tackling and solving the most critical aspects that any logging library
will address, and providing a good interface on which to build more
complex libraries related to logging. If a logging library is to be
added to boost, then 'good enough' is simply not good enough. If I
wanted 'good enough' I can write my own or choose from many of the
libraries out there (including this one).
So what are the minimum requirements? Well certainly there are a few and
there is a good attempt at capturing them here
<http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging>
For me though I wanted to be able to a couple of things and my efforts
at using the library are focused on these.
Essentially I wanted to be able to cheaply define logs in different
scopes in what I would term library code. Therefore users of the library
must be able to use the libraries without logging enabled and pay no
cost. The fact that I have logging statements within my code should be
transparent to them.
At the log site I want to be able to write something like this (or even
better, don't use a macro):
BOOST_LOG(info)
<< "Some message"
<< std::endl;
or perhaps
BOOST_LOG(info,some_log_name)
<< "Some message"
<< std::endl;
A number would also be sufficient instead of a level.
This would be my default usage.
Now I would like the client of the library to be able to enable and
disable my logs in a simple fashion, for example using something like,
boost::logging::enable_logs( "libraryx.*" );
Finally I'd like the client to specify what actually gets logged and in
what format, things like,
time level thread file line function
That iwa my basic requirement. I tried to achieve this with this
library. Now it is not actually possible to do that with this library in
a convenient way because this version, unlike the last, does not support
the notion of named logs in hierarchies. Having said that I had a go
anyway to see how far I could get.
I should also point out that I wanted as painless a way as possible of
using logging (please see past my macro examples... that's what I
started with in this library so it is hard move away from that)
--- for adding log messages ---
1. #include <boost/logging/logging.hpp>
2. BOOST_DEFINE_LOG_IN_CURRENT_SCOPE
or BOOST_DEFINE_NAMED_LOG_IN_CURRENT_SCOPE(some_name)
3. BOOST_LOG(level) << "message" << std::endl;
--- for outputting logged messages ---
1. define a preferred format or simply take a good default
2. say where you want to log to
3. enable the logs you want
--- I ended up writing a small file, <boost/logging/default.hpp> which basically provided wrapper macros for the ones that John has in his library. Here is what I put in it. ----------- #ifndef INCLUDED__boost__logging__default_HPP #define INCLUDED__boost__logging__default_HPP #include <boost/logging/format_fwd.hpp> #include <boost/logging/tag/high_precision_time.hpp> #include <boost/logging/format/formatter/tags.hpp> #include <boost/logging/format/formatter/high_precision_time.hpp> #include <boost/logging/format/formatter/named_spacer.hpp> namespace { typedef boost::logging::tag::holder < boost::logging::optimize::cache_string_one_str<>, boost::logging::tag::high_precision_utc_time, boost::logging::tag::level, boost::logging::tag::file_line, boost::logging::tag::function, boost::logging::tag::thread_id > default_tag_holder_string_t; } BOOST_LOG_FORMAT_MSG( default_tag_holder_string_t ) #define BOOST_LOG_DEFINE_LOGGER(name)\ BOOST_DEFINE_LOG(name,boost::logging::logger_format_write<>) #define BOOST_LOG_DEFINE_FILTER(name)\ BOOST_DEFINE_LOG_FILTER(name,boost::logging::level::holder) #define BOOST_LOG_ADD_DEFAULT_FORMAT_TO_SCOPE(logger_scope)\ logger_scope::logger()->writer().add_formatter\ ( boost::logging::formatter::named_spacer\ ("%time% %level% %file_line% {%function%} [%thread_id%] ")\ .add( "time",\ boost::logging::formatter::\ high_precision_utc_time\ ( "$yyyy-$MM-$dd $hh:$mm:$ss.$micro" ) )\ .add( "level",\ boost::logging::formatter::tag::level() )\ .add( "file_line",\ boost::logging::formatter::tag::file_line() )\ .add( "function",\ boost::logging::formatter::tag::function() )\ .add( "thread_id",\ boost::logging::formatter::thread_id() ) );\ logger_scope::logger()->writer().add_formatter\ ( boost::logging::formatter::append_newline_if_needed() ); #define BOOST_LOG_ADD_FILE_DESTINATION_TO_SCOPE(logger_scope,name)\ logger_scope::logger()->writer().add_destination\ ( boost::logging::destination::file( name ) );\ logger_scope::logger()->mark_as_initialized(); #define BOOST_LOG_ADD_DESTINATION_TO_SCOPE(logger_scope,type) \ logger_scope::logger()->writer().add_destination\ ( boost::logging::destination::type() );\ logger_scope::logger()->mark_as_initialized(); #define BOOST_LOG_DEFINE_LOG_IN_CURRENT_SCOPE\ static BOOST_LOG_DEFINE_LOGGER(logger)\ static BOOST_LOG_DEFINE_FILTER(filter) #define BOOST_LOG(the_level)\ BOOST_LOG_USE_LOG_IF_LEVEL\ ( logger(), filter(), the_level ) \ .set_tag( BOOST_LOG_TAG_LEVEL(the_level) ) \ .set_tag(BOOST_LOG_TAG_FILELINE) \ .set_tag(BOOST_LOG_TAG_FUNCTION) #endif // INCLUDED__boost__logging__default_HPP ----------- Ok, that's pretty hideous and I really don't like using macros, however it did allow me to add reasonable default logging. For example I can now write, --- #include <boost/logging/default.hpp> struct some_class { BOOST_LOG_DEFINE_LOG_IN_CURRENT_SCOPE inline void some_method() { BOOST_LOG(info) << "message" << std::endl; } }; --- To use the log I now write in some initialisation method elsewhere, --- void another_class:: initialise_logging() { boost::filesystem::path ExecutablePath( boost::filesystem::initial_path() ); boost::filesystem::path LogPath( ExecutablePath / "logfile.log" ); std::string LogFile( LogPath.string() ); BOOST_LOG_ADD_DEFAULT_FORMAT_TO_SCOPE (some_class) BOOST_LOG_ADD_FILE_DESTINATION_TO_SCOPE(some_class, LogFile); } --- For every log I want to use I have to add more entries, making this function very long for any remotely complex application. I am not advocating this. I'm merely pointing out that for the usage I was looking for, this library made it hard. In fact I wasn't able to use it as I had hoped and the only way I could get somewhat close was to write yet more macros. I believe this example is important because I see this as probably the most basic use of the logging library beyond simply writing to file directly (or std::cout or anywhere else for that matter). What I presented above is not acceptable, but I do believe the author has the ability to address these issues. In essence I think the above example should be very easy to write, with as few lines as possible and *only* use macros where absolutely necessary. It should be acceptable to use in library code with no impact on performance when logging is disabled. Advanced users with special requirements are then of course welcome to explore the customisation and extension points of the library. Some common customisations and extensions should be provided. Ok, with that context to my review behind us, I'll move onto the review proper. * What is your evaluation of the design? The design as it currently stands does not allow for the simple use case I outline. That does not mean I think it is failed but I do think it shows some important aspects of the design wanting. I do believe this can be addressed. --- As another reader noted concepts are not namespaces. I have no problem with grouping models of the same concept into the same namespace but that is not the same thing. Importantly concepts are named differently than namespaces. I encourage the author to look at how concepts are used in the standard and try to follow that convention. For example 'writer' is an ok name for a namespace but the concept is probably better named MessageWriter. I believe that the concepts used throughout the library could be better named to provide better clarity. I actually found the naming somewhat confusing at times. --- The omission of log hierarchies, or something that allowed the manipulation of several logs at one time, is an unfortunate omission and forced the library to be particularly heavyweight to use. --- Macros were relied on too much, and I realise the author has agreed to address this concern. --- I felt in general the organisation of the library was lacking and some of the defaults surprised me. If a library is to become part of boost one must expect it to work well with other parts of boost. - the default time handling should use the date_time library with easy selection between local time and UTC (which is often preferred) - threading should use the threading library, there should be no roll-your-own code in this important part of the library - the default path should be boost::filesystem::path --- Log Levels: Logging levels should be expressible as both numbers and names (maybe it is and I missed it). It should be possible to provide different name mappings easily (again maybe it is). The set of default levels should be more comprehensive to support a better out-of-box experience and the syslog levels should be part of that set. From <http://tools.ietf.org/html/rfc3164> Numerical Severity Code 0 Emergency: system is unusable 1 Alert: action must be taken immediately 2 Critical: critical conditions 3 Error: error conditions 4 Warning: warning conditions 5 Notice: normal but significant condition 6 Informational: informational messages 7 Debug: debug-level messages I'd suggest adding also, Fatal Severe Notable Verbose Detail Trace These could be ordered as: Fatal Emergency Alert Critical Severe Error Warning Notice Notable Informational Debug Verbose Detail Trace or some could simply be aliases for others. Regardless I shouldn't need to make modifications to use these common names. I'm not suggesting they all be used, simply that they are available. --- The rolling_log is not as I would expect. In most cases where I have used this I have a naming scheme that places the most recent log as the default log name and older logs should have numerically increasing names. Improving the rolling log behaviour should not pose any real difficulties. --- I got bitten by forgetting to use 'mark_as_initialised()'. This seemed symptomatic of the library as a whole. It was very easy to screw up. That implies to me that the interface was not clean enough, or more especially the default use relied on interfaces with too many variation points making it to easy to make a mistake. --- I am concerned about other aspects of the library, but most of these are covered in other reviews. Apologies for not recounting them here, but my time is short. I would say that I would like to see some co-operation between John and Andrey Semashev or at least some of the discussion from Andrey's review explored further particularly with regard to attributes. I am not saying I agree with Andrey, I am merely saying that it would be nice if some more concrete conclusion could be reached. I recognise also that there is likely no one best solution. * What is your evaluation of the implementation? The implementation needs tidied up. I realise this is a work in progress so that in itself is not a big criticism. The code is somewhat dense, hard to read, and the author seems averse to names which spell out what he means. I really see no need for short and cryptic names. Other reviewers have mentioned other things like line lengths and so on. As a side note I strongly encourage the author to install VirtualBox or similar on his Windows machine so he can install Linux as an alternative platform to try this on. * What is your evaluation of the documentation? The documentation has already been discussed at length regarding use of language structure and so on. I'd like to add my voice to those that wanted a clearer structure with more formal language. I'd also like to see that the documentation is easier to navigate. I saw no way to step through topics without having to go back to the main contents page. Some performance comparisons with other libraries may be in order. I am most interested in the case of disabled logging. Once I am actually logging something I am less concerned. At that time I care most about the information I log more than how quick it was (but of course quicker is better). One to three letter names in examples are not acceptable. What does g_l mean? I can *only* guess. * What is your evaluation of the potential usefulness of the library? This is a very important library of immense potential use. Moreover, if for some reason any future boost library itself was complex enough to warrant adding logging to, then this would be the library used. Of course that puts it into the same category as Boost.Test in terms of stability and core 'fit-for-purpose' needs. * Did you try to use the library? With what compiler? Did you have any problems? Yes, gcc 4.2.3, Debian Sid. I didn't encounter any real problems that haven't already been addressed in other reviews. * How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I added logging to a new project and used the log files produced to help track down a problem. I spent time trying to use the library and read the docs to help me with that. I spent a little time looking at the implementation to see if I could make small changes to enable capabilities I saw lacking. * Are you knowledgeable about the problem domain? Somewhat. * Do you think the library should be accepted as a Boost library? Be sure to say this explicitly so that your other comments don't obscure your overall opinion. I've been very encouraged by John's willingness to take on board suggestions and criticisms of his library. His clear enthusiasm to produce a great logging library is very evident. In many ways I'd like to vote yes and let John simply address all the concerns of the library (as he appears both capable of and willing to do). However I think this library is just too important for that to be acceptable. There are other efforts to produce a logging library for boost but so far only John has managed to get a library to review, twice. By and large both libraries have been good ones but both fell short for one reason and another. I think both John and the community has learned a lot from these reviews. I know I certainly learned of some use cases that I had never thought of. I do think John will be able to address most if not all of these with his library. What I would like to see, if possible, is for some of the current efforts on logging to learn from this review and either, join with John to help produce a better library, or get their library ready and into the review queue. As I said at the beginning I have been using John's previous library for some time now and with some success. This new library departed from the first revision perhaps too far for my liking, but once again John has encouraged a good discussion on the topic of a boost.logging library. This is all a very round-about way of saying that I vote Not To Accept this library into boost. But I would like to qualify that. I think the changes that John needs to make are too numerous to warrant a 'yes and tidy-up' vote. I believe there has to be another review. I would be very confident though in a positive outcome of such a review (from my perspective) if John succeeds in addressing the issues raised during review. This can only lead to a better library. I therefore encourage John not to wait too long before asking for his changes to be given a review. I'm sure it won't be a problem getting a review manager. Jamie
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk