logging v1.4 - multiple levels implemented!

Hi all, Just updated the Logging library. Find it here: http://torjo.com/code/logging.zip Also, I've updated the docs. Feedback is welcome. Highlights: **** Multiple levels. **** Easier manipulation of logs **** Modifiers/Appenders can now be deleted. **** Shortcut for BOOST_IS_LOG_ENABLED **** Exception guards **** Shared Memory Appender **** Multiple levels. You can specify levels, very much like log4j. To assign a level to a message, use BOOST_LOGL, like this: BOOST_LOGL(app,dbg) << "some debug message"; BOOST_LOGL(app,err) << "some error"; BOOST_LOGL(gui,warn) << "some GUIsh warning"; Note the BOOST_LOG still works: BOOST_LOG(app) << "some message"; // equivalent to: // BOOST_LOGL(app,default_) << "some message" **** Easier manipulation of logs Every time you need to manipulate some logs (add/delete appenders/modifiers, set the log's level), you use manipulate_logs("logs_spec") Example: manipulate_logs("*") // all logs prefix the message by time .add_modifier(prepend_time("$hh:$mm:$ss "), DEFAULT_INDEX + 1 ) // all log' messages are prefixed by the log name .add_modifier(&prepend_prefix) // all messages are written to cout .add_appender(&write_to_cout); // write all dbg messages and above to app.* logs manipulate_logs("app.*").enable(boost::logging::level::dbg); **** Modifiers/Appenders can now be deleted. In order to delete a modifier/appender, just give it a name: // prepend time, and name this modifier as "time" manipulate_logs("*") .add_modifier(prepend_time("$hh:$mm:$ss "), "time" ); // at a later time BOOST_LOG(app) << "this is prefixed by time"; manipulate_logs("*") .del_modifier("time"); BOOST_LOG(app) << "this is NOT prefixed by time"; ***** Shortcut for BOOST_IS_LOG_ENABLED Instead of saying: // test if message should be written to this log if ( BOOST_IS_LOG_ENABLED(app,some_level)) ...; You can say: if ( app()(some_level)) ...; **** Exception guards When logging, if any modifier/appender throws, the exception is not allowed to propagate. **** Shared Memory Appender An appender that allows writing to a shared memory zone, internally using shmem library. Still to do: **** compile-time logs: write docs about it **** appenders to Event log **** alternate logging manager Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

John Torjo wrote:
Hi all,
Just updated the Logging library. Find it here: http://torjo.com/code/logging.zip Also, I've updated the docs. Feedback is welcome.
Highlights: [snip] **** Multiple levels. You can specify levels, very much like log4j.
To assign a level to a message, use BOOST_LOGL, like this: BOOST_LOGL(app,dbg) << "some debug message"; BOOST_LOGL(app,err) << "some error"; BOOST_LOGL(gui,warn) << "some GUIsh warning";
Note the BOOST_LOG still works: BOOST_LOG(app) << "some message"; // equivalent to: // BOOST_LOGL(app,default_) << "some message"
Hello John, Hi all, This is very good news indeed! In fact, I had integrated the Boost logging last Tuesday in our app, currently employing a workaround for levels (just writing levels as subcategories in brackets, like so: "category.subcategory.[level]"). I am eager to try out the new version. Having just looked at the doc and a little bit at the source code, I couldn't find a way to append (or prepend) the level to the log message. In this regard, one might also want to associate identifying strings with a level (analogous to how this can be done with BOOST_DEFINE_LOG). Having said that, I have to admit that I have just started using the library, and just looked a few minutes at the new version, so there might be solutions to this I haven't thought of. Feedback is very much appreciated. Overall, the handling of levels seems intuitive and easy to use. Many thanks, Andreas

Andreas Wachowski wrote:
Hello John, Hi all,
This is very good news indeed! In fact, I had integrated the Boost logging last Tuesday in our app, currently employing a workaround for levels (just writing levels as subcategories in brackets, like so: "category.subcategory.[level]"). I am eager to try out the new version.
Having just looked at the doc and a little bit at the source code, I couldn't find a way to append (or prepend) the level to the log message. In this regard, one might also want to associate identifying strings with a level (analogous to how this can be done with BOOST_DEFINE_LOG).
I haven't allowed for that yet. It's an interesting idea, and it's not too much development effort. So I guess I'll do it.
Overall, the handling of levels seems intuitive and easy to use.
Thanks! Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

On 8/1/05, John Torjo <john.lists@torjo.com> wrote:
Hi all,
Just updated the Logging library. Find it here: http://torjo.com/code/logging.zip Also, I've updated the docs. Feedback is welcome.
Highlights: **** Multiple levels. **** Easier manipulation of logs **** Modifiers/Appenders can now be deleted. **** Shortcut for BOOST_IS_LOG_ENABLED **** Exception guards **** Shared Memory Appender
**** Multiple levels. You can specify levels, very much like log4j.
Best, John
-- John Torjo, Contributing editor, C/C++ Users Journal
John - this is the first time I've looked at your logging library - looks pretty good (especially now you've got multiple levels - thats something I've used before, which I found very handy). I like the modifiers and appenders as well. A couple of questions - 1. Is it possible to associate different levels with different destinations (e.g. err, fatal and warn with std::cerr, others with std::cout)? 2. Is it possible to query for the current logging level? If logging activities take a significant amount of time, but they won't be output, you don't want to do them. As an example - I've written libraries to decode debug information (Dwarf, Stabs etc). These would at a high logging level, construct and output a textual equivalent of a debug record (which took significant time). The text construction wouldn't be done unless log output was going to be done.

John - this is the first time I've looked at your logging library - looks pretty good (especially now you've got multiple levels - thats something I've used before, which I found very handy). I like the modifiers and appenders as well.
Thanks!
A couple of questions -
1. Is it possible to associate different levels with different destinations (e.g. err, fatal and warn with std::cerr, others with std::cout)?
Not at this time. Basically, levels go like this: - if a message's level is bigger or equal than the log's level, it's outputted. So, I could implement something like this: If a message's level is higher than level X, output it to some destinations. At this time, you specify the destinations per log-hierarchy.
2. Is it possible to query for the current logging level? If logging activities take a significant amount of time, but they won't be output, you don't want to do them. As an example - I've written
For Boost.Log, if a message should not be outputted, logging activities won't take place. It's something like this: // pseudocode if ( !should_write_msg(log,some_level) ; else log.stream() So when you write: BOOST_LOGL(lg,err) << "this " << "is an error" << std::endl; is equivalent to: if ( !should_write_msg(lg,err)) ; else lg.stream() << "this " << "is an error" << std::endl;
libraries to decode debug information (Dwarf, Stabs etc). These would at a high logging level, construct and output a textual equivalent of a debug record (which took significant time). The text construction wouldn't be done unless log output was going to be done.
Is this what you wanted? Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

Hello John, John Torjo wrote:
So, I could implement something like this: If a message's level is higher than level X, output it to some destinations.
At this time, you specify the destinations per log-hierarchy.
I must admit I haven't followed the discussion on log levels closely, but if you add this feature then wouldn't there be any difference between the log hierarchy and log levels? We've been using version 1.3 of the library, which doesn't yet provide log levels, so we simply define log levels by having special logs in the hierarchy. For example, let's say one of our libraries is called Input, then we'd have the following logs declared for it: namespace Input { BOOST_DECLARE_LOG_DEBUG(Debug) BOOST_DECLARE_LOG(Info) BOOST_DECLARE_LOG(Warning) BOOST_DECLARE_LOG(Error) } // namespace Input and defined like this: namespace Input { BOOST_DEFINE_LOG(Debug, "Input.Debug") BOOST_DEFINE_LOG(Info, "Input.Info") BOOST_DEFINE_LOG(Warning, "Input.Warning") BOOST_DEFINE_LOG(Error, "Input.Error") } // namespace Input Then we set up appenders for each log. For example, the debug log only goes to a debut output stream. The info log goes to a file, the warning log goes to a file, the error log goes to a local file and to a socket stream over the network. What additional benefit do we get with the current implementation of log levels? Furthermore, if log levels can have string names and can have different appenders, I don't see the difference to what we're currently doing... Best Regards, Martin TAB Austria Haiderstraße 40 4052 Ansfelden Austria Phone: +43 7229 78040-218 Fax: +43 7229 78040-209 E-mail: martin.ecker@tab.at http://www.tab.at

I must admit I haven't followed the discussion on log levels closely, but if you add this feature then wouldn't there be any difference between the log hierarchy and log levels? [...] What additional benefit do we get with the current implementation of log levels? Furthermore, if log levels can have string names and can have different appenders, I don't see the difference to what we're currently doing...
It seems that people want, in addition to several logs, the ability to write to each log, given levels. I don't fancy levels, but a lot of people requested them. They do have a nice ring to it. Then, each module can have its own log, and you can write all sorts of info to each log: BOOST_LOGL(gui,dbg) << "debug message"; BOOST_LOGL(gui,err) << "error message"; ... Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

On 8/5/05, John Torjo <john.lists@torjo.com> wrote: <snip>
2. Is it possible to query for the current logging level? If logging activities take a significant amount of time, but they won't be output, you don't want to do them. As an example - I've written
For Boost.Log, if a message should not be outputted, logging activities won't take place.
It's something like this: // pseudocode if ( !should_write_msg(log,some_level) ; else log.stream()
So when you write:
BOOST_LOGL(lg,err) << "this " << "is an error" << std::endl;
is equivalent to:
if ( !should_write_msg(lg,err)) ; else lg.stream() << "this " << "is an error" << std::endl;
libraries to decode debug information (Dwarf, Stabs etc). These would at a high logging level, construct and output a textual equivalent of a debug record (which took significant time). The text construction wouldn't be done unless log output was going to be done.
Is this what you wanted?
Best, John
John - you should have told me to RTFM! I've found exactly what I wanted on the 'Efficiency' page. This is the sort of thing I want to do: if (<some-log>()(boost::logging::level::my_error)) { std::ostringstream oss; // v is a vector<int> std::copy(v.begin(), v.end(), std::ostream_iterator<int>(oss, ", ")); BOOST_LOGL(<some-log>, my_error) << oss.str() << "\n"; } Now, that example can be modified to write to the logging stream directly using this: std::copy(v.begin(), v.end(), std::ostream_iterator<int>(*<some-log>().stream().stream(), ", ")); BOOST_LOGL(<some-log>, my_error) << "\n"; First question - is this usage that you envisaged? . Secondly, the output is 09:30:35 [app] 0, 1, 2, 3, 09:30:35 [app] rather than 09:30:35 [app] 0, 1, 2, 3, because of the second log statement prepending the time before the "\n". Is there any way of getting the required output using the above code pattern, by somehow stopping the log modifiers? Stuart Dootson

Now, that example can be modified to write to the logging stream directly using this:
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(*<some-log>().stream().stream(), ", ")); BOOST_LOGL(<some-log>, my_error) << "\n";
First question - is this usage that you envisaged? . Secondly, the output is
09:30:35 [app] 0, 1, 2, 3, 09:30:35 [app]
rather than
09:30:35 [app] 0, 1, 2, 3,
because of the second log statement prepending the time before the "\n". Is there any way of getting the required output using the above code pattern, by somehow stopping the log modifiers?
I guess the simplest way is to write to a std::ostringstream first. Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

John Torjo wrote:
Hi all,
Just updated the Logging library. Find it here: http://torjo.com/code/logging.zip Also, I've updated the docs. Feedback is welcome.
While trying out the new library, I found the need to apply a small patch to make levels work with scopeed loggers: In boost/log/log.hpp: 390c390 << #define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(log_name,::boost::logging::level::lvl)) ; else (*keep.stream()) ---
#define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(::boost::logging::logger_and_level(log_name,::boost::logging::level::lvl)) ) ; else (*keep.stream())
Note the use of the "logger_and_level" constructor. Best regards, Andreas

I seem to be having problems using log library 1.4 with my log library test code I used with log library 1.33. I applied Andreas' fix below but still have a couple problems... quite possibly my own as I'm new to using boost. Has anyone seen problems like these? Linking issues: --> One program insists it can't find the .lib. --> A simpler program linked, but on running windows claimed it could not find add_appender in the .dll. --> It looks like the older syntax for add_appender is still there enough to compile and link but maybe not run? I also had trouble with the new syntax. Then I tried the sample code from basic_usage.html Compilation issues: the sample code in basic_usage.html doesn't seem to work. The signature for add_modifier doesn't seem right - I think the first string parameter has to be removed. Now off to try the sample code! Andreas Wachowski wrote: [snip]
390c390 << #define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(log_name,::boost::logging::level::lvl)) ; else (*keep.stream()) ---
#define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(::boost::logging::logger_and_level(log_name,::boost::logging::level::lvl)) ) ; else (*keep.stream())
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Andy Schweitzer wrote:
I seem to be having problems using log library 1.4 [snip] Now off to try the sample code! [snip]
Tried sample projects. They all build and run, with following comments: -->shmem_example projects seem to be missing shmem_named_shared_object.hpp -->multiple_threads references a ts.cpp file that's not there, but builds without it. multiple_threas also has assertions. -->simple crashes at line 51 on my system in strlen. -->these needed their library directories updated: fast_compile logbreak_example multiple_threads rotating_logs

They all build and run, with following comments:
-->shmem_example projects seem to be missing shmem_named_shared_object.hpp
This works only if you have the shmem library as well.
-->multiple_threads references a ts.cpp file that's not there, but builds without it. multiple_threas also has assertions.
Ok, my bad :) Fixed now.
-->simple crashes at line 51 on my system in strlen.
Again my bad. Fixed onw.
-->these needed their library directories updated: fast_compile logbreak_example multiple_threads rotating_logs
Not sure why, they build find on my machine. Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

Andy Schweitzer wrote:
I seem to be having problems using log library 1.4 [snip]
Linking issues: --> One program insists it can't find the .lib. --> A simpler program linked, but on running windows claimed it could not find add_appender in the .dll.
[snip] I think the first problem was due to not generating the multi-threaded log libaries with bjam. I'm not sure why they are not getting generated, I must be doing something wrong. I'm not sure what the second problem was due to, but creating a new VC project from scratch made it go away. On another topic, manipulating_logs.html seems to imply that enable_logs, disable_logs should be called through manipulate_logs, but they don't seem to be implemented that way yet. The bare function calls still work though.

I think the first problem was due to not generating the multi-threaded log libaries with bjam. I'm not sure why they are not getting generated, I must be doing something wrong.
I'm not sure what the second problem was due to, but creating a new VC project from scratch made it go away.
Maybe you can send me (privately) a trimmed down project and I'll check it out. Anyway, it might be a while until I find the time to do it -- I'm quite busy at the moment.
On another topic, manipulating_logs.html seems to imply that enable_logs, disable_logs should be called through manipulate_logs, but
My mistake. They're now called enable() and disable().
they don't seem to be implemented that way yet. The bare function calls still work though.
Yes, they still do. I wanted to be backwards compatible. Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!

While trying out the new library, I found the need to apply a small patch to make levels work with scopeed loggers:
In boost/log/log.hpp:
390c390 << #define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(log_name,::boost::logging::level::lvl)) ; else (*keep.stream()) ---
#define BOOST_SCOPEDLOGL(log_name,lvl) if (::boost::logging::simple_logger_keeper keep = ::boost::logging::simple_logger_keeper(::boost::logging::logger_and_level(log_name,::boost::logging::level::lvl)) ) ; else (*keep.stream())
Note the use of the "logger_and_level" constructor.
Yes, thanks! Applied it, and will put it on the site, shortly. Best, John -- John Torjo, Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -v1.6.3 (Resource Splitter) -- http://www.torjo.com/cb/ - Click, Build, Run!
participants (5)
-
Andreas Wachowski
-
Andy Schweitzer
-
John Torjo
-
martin.ecker@tab.at
-
Stuart Dootson