Boost logo

Boost :

From: John Torjo (john.lists_at_[hidden])
Date: 2004-06-28 13:39:16


Dear boosters,

Finally, found some time to work on the logging library ;)
(for those interested - find it at
http://groups.yahoo.com/group/boost/files/logging.zip or
http://www.torjo.com/code/logging.zip)
note: this first draft works only for Win32 :(

In short, it makes it very easy to log messages, while being efficient.
And on top of that, it's thread-safe.

Table of contents:
****** Declaring/defining logs
****** Log hierarchies
****** Modifier/ Log functions
****** Enabling/disabling of logs
****** Example

****** Declaring/defining logs
Every log has:
- a unique ID, by which you refer when you write to it
- a name, by which you can modify its behavior (for example,
enabling/disabling). The name is very useful so that you can manipulate
multiple logs at once (for instance, disable multiple logs at once) and
to be externally known - to easily manipulate logs at run-time. Also,
this way is also easier to form hierarchies of logs (see below).

Cutting to the chanse:
// declare a log, in a header file
BOOST_DECLARE_LOG(dbg)

// define the log, in a source file
BOOST_DEFINE_LOG(dbg, "app.dbg")

// writing to the log
BOOST_LOG(dbg) << "this is a debug message, i=" << i << std::endl;

****** Log hierarchies
Logs can be hierarchical. In a hierarchy, a log inherits all anscestors'
behaviors. Log A is said to be a descendant of Log B if when B's name
starts with A's name (example: "app.charts.gui.dbg" is a descendant of
"app.charts", but not a descendant of "app.err")

When manipulating logs, you're always manipulating a hierarchy - the
wildcard '*' stands for "anything" - see below.

****** Modifier/ Log functions

When a message is written to a log, the following will happen:
- first, check if the log is enabled. If so, continue. Otherwise, do
nothing.
- run all modifiers for this message
- run all log functions for this message

A modifier is a function that can modify the original message before
it's printed to any destination. Examples can be: prefix the message
with the current time, or prefix the message with the current thread ID.

The log function is a function that will actually print this message to
a destination. An example:
void write_to_cout(const std::string &, const std::string &msg) {
     std::cout << msg;
}

When setting modifier/log functions, you're manipulating hierarchies of
logs. Example:
// add this modifier to all logs that start with "app" name
add_modifier_func("app*", &write_prefix);
// write all messages to cout
add_log_func("*", write_to_cout);

****** Enabling/disabling of logs
By default, all logs are enabled. You can specifically enable/disable
some of them - wildcards work.

Example:
// disable all logs
disable_logs("*");
// enable all dbg logs
enable_logs("*dbg*");

Note that enabling/disabling is VERY EFFICIENT. In case a log is not
enabled, the code after BOOST_LOG(...) is not executed AT ALL.
Example:
BOOST_LOG(app) << some_time_consuming_msg << std::endl;

If 'app' is disabled, some_time_consuming_msg will not be executed.

****** Example
// full sample found in logging.zip
// Modifiers for all:
// [type_of_message] original_message append_enter_if_needed
add_modifier_func("*", &prefix_time, std::numeric_limits<int>::max() );
add_modifier_func("*", &append_enter);
// Modifiers for app and its ascendants
// <time> [type_of_message] original_message append_enter_if_needed
add_modifier_func("app*", &write_prefix);
// Modifiers for "app" only
// <time> [Thread ID] [type_of_message] original_message
append_enter_if_needed
add_modifier_func("app", &prepend_thread_id, 0);

// Log Functions
// all messages are written to cout
add_log_func("*", write_to_cout);
// "app*" messages are written to file as well
add_log_func("app*", write_to_file);
// 'app' only and dbg messages are written to Output Debug Window as well
add_log_func("app", write_to_dbg_wnd);
add_log_func("*dbg*", write_to_dbg_wnd);

int i = 1, j = 2, k = 3;
BOOST_LOG(app) << "testing" << i << '-' << j << '-' << k;
// written to both cout & the file & Output Debug Window
BOOST_LOG(dbg) << "this is a debug message, i=" << i << std::endl;

BOOST_LOG(info) << "I just wanted to tell you something....";
BOOST_LOG(warn) << "Logged-on Users approach max. limit";
BOOST_LOG(err) << "Too many users!";

BOOST_LOG(charts::gui) << "Creating main window" << std::endl;
BOOST_LOG(charts::dbg) << "A debug msg coming from {charts} module" ;
BOOST_LOG(charts::gui) << "Destroying main window" << std::endl;

// disable all descendants of 'app' (not the 'app' itself)
disable_logs("app.*");
BOOST_LOG(dbg) << "this won't be written" << std::endl;
BOOST_LOG(app) << "However, this msg. will" << std::endl;
enable_logs("app.dbg"); // specifically, only dbg log is enabled back now
BOOST_LOG(dbg) << "this will be written - this log just got enabled" <<
std::endl;
BOOST_LOG(err) << "this still won't be written" << std::endl;
enable_logs("app.*");
disable_logs("app.dbg");
// now, all logs are back to the 'enabled' state
BOOST_LOG(err) << "this will be written - all logs are enabled" <<
std::endl;

// disable all logs
disable_logs("*");
BOOST_LOG(err) << "this won't be written" << std::endl;
BOOST_LOG(app) << "neither will this" << std::endl;
BOOST_LOG(dbg) << "or this..." << std::endl;
BOOST_LOG(warn) << "or this..." << std::endl;
BOOST_LOG(info) << "or this..." << std::endl;

// enable all dbg logs
enable_logs("*dbg*");
BOOST_LOG(app) << "this won't be written" << std::endl;
BOOST_LOG(dbg) << "this will be written" << std::endl;
BOOST_LOG(info) << "this won't be written" << std::endl;
// enable info log
enable_logs("*info*");
BOOST_LOG(info) << "a simple info" << std::endl;

/*** Console

     18:59:44 [app] [Thread 1384] testing1-2-3
     18:59:44 [app.dbg] this is a debug message, i=1
     18:59:44 I just wanted to tell you something....
     18:59:44 [app.warn] Logged-on Users approach max. limit
     18:59:44 [app.err] Too many users!
     18:59:44 Creating main window
     18:59:44 A debug msg coming from {charts} module
     18:59:44 Destroying main window
     18:59:44 [app] [Thread 1384] However, this msg. will
     18:59:44 [app.dbg] this will be written - this log just got enabled
     18:59:44 [app.err] this will be written - all logs are enabled
     18:59:44 [app.dbg] this will be written
     18:59:44 a simple info
*/

/*** Output Debug Window

     19:00:03 [app] [Thread 1384] testing1-2-3
     19:00:03 [app.dbg] this is a debug message, i=1
     19:00:03 A debug msg coming from {charts} module
     19:00:03 [app] [Thread 1384] However, this msg. will
     19:00:03 [app.dbg] this will be written - this log just got enabled
     19:00:03 [app.dbg] this will be written
*/

/*** out.txt file

     19:00:03 [app] [Thread 1384] testing1-2-3
     19:00:03 [app.dbg] this is a debug message, i=1
     19:00:03 [app.warn] Logged-on Users approach max. limit
     19:00:03 [app.err] Too many users!
     19:00:03 [app] [Thread 1384] However, this msg. will
     19:00:03 [app.dbg] this will be written - this log just got enabled
     19:00:03 [app.err] this will be written - all logs are enabled
     19:00:03 [app.dbg] this will be written
*/

There's some more work - the docs also ;) , but I'm pretty happy with
the first draft. What do you all think?

Best,
John

-- 
John Torjo
Freelancer
-- john_at_[hidden]
-- http://www.torjo.com/logview/ - viewing/filtering logs is just too easy!

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk