/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING $Log$ Revision 1.3 2005/11/29 12:39:42 marc make it compilable with gcc 4.0.2 and newer doxygen Revision 1.2 2005/04/07 20:50:13 marc docu: new doxygen, new grouping Revision 1.1 2005/03/11 21:07:54 marc initial version 1 2 3 4 5 6 7 8 5678901234567890123456789012345678901234567890123456789012345678901234567890 */ #ifndef __MRW_FUNCTIONTRACE_HPP__ #define __MRW_FUNCTIONTRACE_HPP__ #include #include #include #include // support for log4cxx 0.9.7 and for new CVS version #ifndef LOG4CXX_LOCATION # define MRW_LOG4CXX_LOCATION _file.c_str(), _line #define MRW_LEVEL_DEBUG ::log4cxx::Level::DEBUG #define MRW_LEVEL_INFO ::log4cxx::Level::INFO #define MRW_LEVEL_WARN ::log4cxx::Level::WARN #define MRW_LEVEL_ERROR ::log4cxx::Level::ERROR #define MRW_LEVEL_FATAL ::log4cxx::Level::FATAL #else # define LOG4CXX_CVS # define MRW_LOG4CXX_LOCATION \ ::log4cxx::spi::LocationInfo(_file.c_str(), _name.c_str(), _line) #define MRW_LEVEL_DEBUG ::log4cxx::Level::getDebug() #define MRW_LEVEL_INFO ::log4cxx::Level::getInfo() #define MRW_LEVEL_WARN ::log4cxx::Level::getWarn() #define MRW_LEVEL_ERROR ::log4cxx::Level::getError() #define MRW_LEVEL_FATAL ::log4cxx::Level::getFatal() #endif namespace mrw { /** @addtogroup debug */ //@{ /** @defgroup FunctionTrace Function Tracing (using log4cxx) @pre \#include Place the macro @ref MRW_METHOD as first line of all methods you want to trace, and macro @ref MRW_FUNCTION as first line of all functions to be traced. There are alternative macros @ref MRW_METHOD2 and @ref MRW_FUNCTION2, that allow you to also specify the logging hierarchy. The default for the hierarchy string is @c "mrw.fntrace". The logging level is always @c DEBUG and cannot be changed. @attention Be careful when you copy paste the declarations from one method to the other! Don't forget to correctly change the method's name! I recommend to use a tool like OpenC++ (http://opencxx.sf.net) to automatically place function trace statements into classes, so that the coder does not have to care about. I have already partly written such a module and I will provide it for free (GPL) when it is good enough. The trace of the following code: @code \#include \#include void fn(int i=0) { MRW_FUNCTION("fn(int)"); // trace entry and exit if (i<4) fn(++i); } class A { public: A() { // not traced method(); } void method() { MRW_METHOD("A::method()"); // trace entry and exit fn(); } }; int main(int, char**) { log4cxx::BasicConfigurator::configure(); MRW_FUNCTION("main(int, char**)"); A().method(); return 0; } @endcode Produces this output: @verbatim 1 [1078671488] DEBUG mrw.fntrace - \ main(int, char**) 1 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: \ A::method() 1 [1078671488] DEBUG mrw.fntrace - \ fn(int) 2 [1078671488] DEBUG mrw.fntrace - \ fn(int) 2 [1078671488] DEBUG mrw.fntrace - \ fn(int) 2 [1078671488] DEBUG mrw.fntrace - \ fn(int) 2 [1078671488] DEBUG mrw.fntrace - \ fn(int) 2 [1078671488] DEBUG mrw.fntrace - / fn(int) 2 [1078671488] DEBUG mrw.fntrace - / fn(int) 2 [1078671488] DEBUG mrw.fntrace - / fn(int) 3 [1078671488] DEBUG mrw.fntrace - / fn(int) 3 [1078671488] DEBUG mrw.fntrace - / fn(int) 3 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: / A::method() 3 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: \ A::method() 3 [1078671488] DEBUG mrw.fntrace - \ fn(int) 3 [1078671488] DEBUG mrw.fntrace - \ fn(int) 3 [1078671488] DEBUG mrw.fntrace - \ fn(int) 4 [1078671488] DEBUG mrw.fntrace - \ fn(int) 4 [1078671488] DEBUG mrw.fntrace - \ fn(int) 4 [1078671488] DEBUG mrw.fntrace - / fn(int) 4 [1078671488] DEBUG mrw.fntrace - / fn(int) 4 [1078671488] DEBUG mrw.fntrace - / fn(int) 4 [1078671488] DEBUG mrw.fntrace - / fn(int) 4 [1078671488] DEBUG mrw.fntrace - / fn(int) 4 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: / A::method() 5 [1078671488] DEBUG mrw.fntrace - / main(int, char**) @endverbatim The indentation is according to the call stack depth level, the name is preceded by a @c \\ on entry and by a @c / on exit. The methods are preceded by the address of @c this, so that different instances can be distinguished. All before and including the dash @c - is configurable through the normal log4cxx trace patterns. */ //@{ /** @brief Declare method entrance, place as first line in method. Place this macro as first statement in each method that should write a function trace. Example: @code namespace xxx { namespace yyy { class Zzz { public: void doIt(int, char) { MRW_METHOD("xxx::yyy::Zzz::doIt(int, char)"); [...] } }; } } @endcode @param name the full name of the method, including namespaces and class name */ #define MRW_METHOD(name) mrw::FnTrace fnTrace(this, name, __FILE__, __LINE__) /** @brief Declare function entrance, place as first line in functions. Place this macro as first statement in each function that should write a function trace. Example: @code namespace xxx { namespace yyy { void doIt(int, char) { MRW_FUNCTION("xxx::yyy::doIt(int, char)"); [...] } } } @endcode @param name the full name of the function, including namespaces */ #define MRW_FUNCTION(name) mrw::FnTrace fnTrace(name, __FILE__, __LINE__) /** @brief Declare method entrance, place as first line in method. Place this macro as first statement in each method that should write a function trace. This macro allows you to define a logging hierarchy string. Example: @code namespace xxx { namespace yyy { class Zzz { public: void doIt(int, char) { MRW_METHOD2("xxx::yyy::Zzz::doIt(int, char)", "fn.xxx.yyy.Zzz"); [...] } }; } } @endcode @param name the full name of the method, including namespaces and class name @param tracer the tracer hierarchy */ #define MRW_METHOD2(name, tracer) \ mrw::FnTrace fnTrace(this, name, __FILE__, __LINE__, tracer) /** @brief Declare function entrance, place as first line in functions. Place this macro as first statement in each function that should write a function trace. This macro allows you to define a logging hierarchy string. Example: @code namespace xxx { namespace yyy { void doIt(int, char) { MRW_FUNCTION2("xxx::yyy::doIt(int, char)", "fn.xxx.yyy"); [...] } } } @endcode @param name the full name of the function, including namespaces @param tracer the tracer hierarchy */ #define MRW_FUNCTION2(name, tracer) \ mrw::FnTrace fnTrace(name, __FILE__, __LINE__, tracer) class FnTrace { public: FnTrace(const void* addr, const std::string& name, const std::string& file, unsigned long line, const std::string& tracer = "mrw.fntrace") throw(): _addr(addr), _name(name.begin(), name.end()), _file(file), _line(line), _tracer(tracer.begin(), tracer.end()) { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { std::basic_stringstream oss; oss<forcedLog(MRW_LEVEL_DEBUG, oss.str(), MRW_LOG4CXX_LOCATION); ++_level; } } FnTrace(const std::string& name, const std::string& file, unsigned long line, const std::string& tracer = "mrw.fntrace") throw(): _addr(0), _name(name.begin(), name.end()), _file(file), _line(line), _tracer(tracer.begin(), tracer.end()) { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { std::basic_stringstream oss; oss<forcedLog(MRW_LEVEL_DEBUG, oss.str(), MRW_LOG4CXX_LOCATION); ++_level; } } ~FnTrace() throw() { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { --_level; std::basic_stringstream oss; if (_addr) oss<forcedLog(MRW_LEVEL_DEBUG, oss.str(), MRW_LOG4CXX_LOCATION); } } private: const void* _addr; const log4cxx::String _name; const std::string _file; unsigned long _line; const log4cxx::String _tracer; /** @todo for multithreading, use thread specific storage */ static unsigned int _level; }; //@} //@} } #endif