
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@torjo.com -- http://www.torjo.com/logview/ - viewing/filtering logs is just too easy!