309 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|     $Id$
 | |
| 
 | |
|     $Date$
 | |
|     $Author$
 | |
| 
 | |
|     @copy © Marc Wäckerlin
 | |
|     @license LGPL, see file <a href="license.html">COPYING</a>
 | |
| 
 | |
|     $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 <log4cxx/logger.h>
 | |
| #include <sstream>
 | |
| #include <iomanip>
 | |
| #include <string>
 | |
| 
 | |
| // 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 <mrw/functiontrace.hpp>
 | |
|       
 | |
|       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 <mrw/functiontrace.hpp>
 | |
|       \#include <log4cxx/basicconfigurator.h>
 | |
|       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<log4cxx::String::value_type> oss;
 | |
|           oss<<std::hex<<std::setw(15)<<_addr<<": "
 | |
|              <<std::dec<<std::setw(2+_level)
 | |
|              <<std::setfill(log4cxx::String::value_type(' '))<<"\\ "<<_name;
 | |
|           logger->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<log4cxx::String::value_type> oss;
 | |
|           oss<<std::setw(17)<<' '
 | |
|              <<std::setw(2+_level)<<std::setfill(log4cxx::String::value_type(' '))
 | |
|              <<"\\ "<<_name;
 | |
|           logger->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<log4cxx::String::value_type> oss;
 | |
|           if (_addr)
 | |
|             oss<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec;
 | |
|           else
 | |
|             oss<<std::setw(17)<<' '; 
 | |
|           oss<<std::setw(2+_level)<<std::setfill(log4cxx::String::value_type(' '))
 | |
|              <<"/ "<<_name;
 | |
|           logger->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
 |