/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING $Log$ 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 namespace mrw { /** @addtogroup debug */ //@{ /** @defgroup FunctionTrace Function Tracing (using log4cxx) @pre #include Place the macro @ref METHOD as first line of all methods you want to trace, and macro @ref FUNCTION as first line of all functions to be traced. There are alternative macros @ref METHOD2 and @ref 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) { FUNCTION("fn(int)"); // trace entry and exit if (i<4) fn(++i); } class A { public: A() { // not traced method(); } void method() { METHOD("A::method()"); // trace entry and exit fn(); } }; int main(int, char**) { log4cxx::BasicConfigurator::configure(); 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) { METHOD("xxx::yyy::Zzz::doIt(int, char)"); [...] } }; } } @endcode @param name the full name of the method, including namespaces and class name */ #define 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) { FUNCTION("xxx::yyy::doIt(int, char)"); [...] } } } @endcode @param name the full name of the function, including namespaces */ #define 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) { 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 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) { 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 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), _file(file), _line(line), _tracer(tracer) { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { std::stringstream oss; oss<forcedLog(log4cxx::Level::DEBUG, oss.str(), _file.c_str(), _line); ++_level; } } FnTrace(const std::string& name, const std::string& file, unsigned long line, const std::string& tracer = "mrw.fntrace") throw(): _addr(0), _name(name), _file(file), _line(line), _tracer(tracer) { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { std::stringstream oss; oss<forcedLog(log4cxx::Level::DEBUG, oss.str(), _file.c_str(), _line); ++_level; } } ~FnTrace() throw() { log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer)); if (logger->isDebugEnabled()) { --_level; std::stringstream oss; if (_addr) oss<forcedLog(log4cxx::Level::DEBUG, oss.str(), _file.c_str(), _line); } } private: const void* _addr; const std::string _name; const std::string _file; unsigned long _line; const std::string _tracer; /** @todo for multithreading, use thread specific storage */ static unsigned int _level; }; //@} //@} } #endif