| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Logging and Tracing in C++ Simplified: Traveling the Road Last Traveled by a Previously Invented Wheelby Mo Budlong THIS INFORMATION AND EXAMPLES OF CODE ARE BEING PROVIDED BY SUN AS A COURTESY, "AS IS," AND SUN DISCLAIMS ANY AND ALL WARRANTIES PERTAINING THERETO, INCLUDING ANY WARRANTIES OF MERCHANTABILTY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. Summary
Tracing and logging is a common aid to, and sometimes a critical path for, debugging. The Open Source Logging and TracingYou should trace. Got it? Sure you do, but back up a bit. If you write an application of more than 500 lines, or the application runs as a daemon or service, or you want to save yourself a lot of debugging headaches - you should trace. Which, brings you back around to the original statement. You should trace. Ah, but, how does a C++ program do its tracing? If you came up through the ranks of procedural languages, that is C, you are probably used to the old:
<listing> #ifdef DEBUG fprintf(trace_file,"Here is a useless trace message\n"); #endif </listing> But object oriented languages are uh . . . object oriented. Spewing random messages to a trace file isn't quite elegant enough, not to mention just plain not objectivied, objectilated, . . . whatever. You need a tracing and logging class.
No, no, it's not written here for you because it's already taken care of. Ceki Gülcü created a powerful set of logging and tracing objects in Java[tm] named Inserting logging and tracing into code has been around since code was first authored using a hammer and chisel. The technique is low-tech, but it is also very effective. If it is done correctly, it can even form the basis of auditing the software and become an effective error logging tool.
Logging can slow down a program, and the authors of Metrics are hard to quote, because of the wide variance in the volume of trace messages. At one end you can trace only the severe messages <listing> #ifdef DEBUG trace("Captain, the dilithium crystals are melting\n"); #endif </listing> At the other, you can trace almost every line of code.
The important thing for a tracing class is that its mere existence in an application should not unduly slow down the application. The
Where So, if you are interested in logging and tracing and would like to use an excellent and freely available tracing tool, read on.
A layout class controls what the output message is going to look like. You may derive your own classes from
An appender class writes the trace message out to some device. The messages have been formatted by a layout object.
A category class does the actual logging. The two main parts of a category are its appenders and its priority. Priority controls which messages can be logged by a particular class. When a category object is created, it starts life with a default appender of standard out and a default priority of none. One or more appenders can be added to the list of destinations for logging. There will be more on appenders in a moment. The priority of a category can be set to <listing> NOTSET < DEBUG < INFO < NOTICE < WARN < ERROR < CRIT < ALERT < FATAL = EMERG </listing>
Messages can be given any of these priorities except
So if a category has been set to level
Categories are arranged in a tree with the root node being the top level category. This allows you to break tracing down into layers (categories). Children categories, when first created, inherit the priority of their parent category. In practice this is not very useful, but you have not yet fully exercised Appenders accumulate as a collection on a category object. When an appender is added to a category, the default behavior is for the appender to be added to the list of destinations for logging messages. It would be possible to log/trace into two files simultaneously as well as echoing to standard out.
In practice you rarely want to log to more than one place at a time, and the single destination that you do want is almost never standard out. How does all this hang together? The Category class creates one overall static root category object (called root). The application can create instances of children categories from this one static parent.
Here is a simple example of using
Unless you want to use the basic logging setup of a simple layout sent to standard out, there are six initial steps to using a
<listing> // TestLog4CPP.cpp : A small exerciser for log4cpp #include "Category.hh" #include "FileAppender.hh" #include "BasicLayout.hh" int main(int argc, char* argv[]) { // 1 instantiate an appender object that // will append to a log file log4cpp::Appender* app = new log4cpp::FileAppender("FileAppender", "/logs/testlog4cpp.log"); // 2. Instantiate a layout object // Two layouts come already available in log4cpp // unless you create your own. // BasicLayout includes a time stamp log4cpp::Layout* layout = new log4cpp::BasicLayout(); // 3. attach the layout object to the // appender object app->setLayout(layout); // 4. Instantiate the category object // you may extract the root category, but it is // usually more practical to directly instance // a child category log4cpp::Category main_cat = log4cpp::Category::getInstance("main_cat"); // 5. Step 1 // an Appender when added to a category becomes // an additional output destination unless // Additivity is set to false when it is false, // the appender added to the category replaces // all previously existing appenders main_cat.setAdditivity(false); // 5. Step 2 // this appender becomes the only one main_cat.setAppender(app); // 6. Set up the priority for the category // and is given INFO priority // attempts to log DEBUG messages will fail main_cat.setPriority(log4cpp::Priority::INFO); // so we log some examples main_cat.info("This is some info"); main_cat.debug("This debug message will fail to write"); main_cat.alert("All hands abandon ship"); // you can log by using a log() method with // a priority main_cat.log(log4cpp::Priority::WARN, "This will be a logged warning"); // gives you some programmatic control over // priority levels log4cpp::Priority::PriorityLevel priority; bool this_is_critical = true; if(this_is_critical) priority = log4cpp::Priority::CRIT; else priority = log4cpp::Priority::DEBUG; // this would not be logged if priority // == DEBUG, because the category priority is // set to INFO main_cat.log(priority,"Importance depends on context"); // You may also log by using stream style // operations on main_cat.critStream() << "This will show up << as " << 1 << " critical message" << log4cpp::CategoryStream::ENDLINE; main_cat.emergStream() << "This will show up as " << 1 << " emergency message" << log4cpp::CategoryStream::ENDLINE; // Stream operations can be used directly // with the main object, but are // preceded by the severity level main_cat << log4cpp::Priority::ERROR << "And this will be an error" << log4cpp::CategoryStream::ENDLINE; // This illustrates a small bug in version // 2.5 of log4cpp main_cat.debug("debug"); // this is correctly // skipped main_cat.info("info"); main_cat.notice("notice"); main_cat.warn("warn"); main_cat.error("error"); main_cat.crit("crit"); // this prints ALERT // main_cat : crit main_cat.alert("alert");// this prints PANIC // main_cat : alert main_cat.emerg("emerg");// this prints UNKOWN // main_cat : emerg main_cat.debug("Shutting down");// this will // be skipped // clean up and flush all appenders log4cpp::Category::shutdown(); return 0; } </listing>
The final section of the code in this example illustrates a bug in <listing> date_time PRIORITY category_name : message </listing>
The following listing is an example of the output from this program. Note that the <listing> 995871335 INFO main_cat : This is some info 995871335 PANIC main_cat : All hands abandon ship 995871335 WARN main_cat : This will be a logged warning 995871335 ALERT main_cat : Importance depends on context 995871335 ALERT main_cat : This will show up as 1 critical message 995871335 UNKOWN main_cat : This will show up as 1 emergency message 995871335 ERROR main_cat : And this will be an error 995871335 INFO main_cat : info 995871335 NOTICE main_cat : notice 995871335 WARN main_cat : warn 995871335 ERROR main_cat : error 995871335 ALERT main_cat : crit 995871335 PANIC main_cat : alert 995871335 UNKOWN main_cat : emerg </listing> So perhaps last but not least, you come to the beauty of open source. Found a bug? Just go ahead and fix it.
The following listing is a copy of the The name of a priority is created from a static array and an index created based on the priority number. <listing> /* * Priority.cpp * * Copyright 2000, LifeLine Networks BV * (www.lifeline.nl). All rights reserved. * Copyright 2000, Bastiaan Bakker. All rights reserved. * See the COPYING file for the terms of usage and * distribution. */ #include "Portability.hh" #include "Priority.hh" namespace log4cpp { const std::string& Priority::getPriorityName(int priority) throw() { static std::string names[9] = { "PANIC", "ALERT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG", "NOTSET", "UNKOWN" }; priority++; priority /= 100; return ((priority < 1) || (priority > 8)) ? names[8] : names[priority - 1]; } } </listing>
But the priority numbers are slightly different than this function expects as shown below in an extract from <listing> typedef enum {EMERG = 0, FATAL = 0, ALERT = 100, CRIT = 200, ERROR = 300, WARN = 400, NOTICE = 500, INFO = 600, DEBUG = 700, NOTSET = 800 } PriorityLevel; </listing> So the fix is to adjust the array and code as follows (Changes are underlined) <listing> /* * Priority.cpp * * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). * All rights reserved. * Copyright 2000, Bastiaan Bakker. All rights reserved. * * See the COPYING file for the terms of usage and * distribution. */ #include "Portability.hh" #include "Priority.hh" namespace log4cpp { const std::string& Priority::getPriorityName(int priority) throw() { static std::string names[10] = { "PANIC", "ALERT", "CRITICAL", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG", "NOTSET", "UNKOWN" }; priority++; priority /= 100; return ((priority < 0) || (priority > 8)) ? names[9] : names[priority]; } } </listing>
It is still missing one critical piece. The Java[tm] version Does this give you any ideas? Resources
sourceforge.net is an excellent resource for open source software. As well as
The introduction to using About the AuthorMo Budlong is president of King Computer Services and has been creating utilities, applications, and client server solutions on UNIX® boxes for over 15 years. He has published numerous books and articles on subjects ranging from Assembly language to XML. He is the author of the web based UNIX 101 column, and is soon publishing a book on UNIX basics. Mo is also a musician who plays guitar, bass, and keyboards, and messes around with several other instruments. He lives in Southern California with this wife, daughter, and The Cat. August 2001
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|