/** @file

    $Id$

    $Date$
    $Author$

    @copy &copy; Marc W&auml;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
#else
#  define LOG4CXX_CVS
#  define MRW_LOG4CXX_LOCATION \
  ::log4cxx::spi::LocationInfo(_name.c_str(), _file.c_str(), _line)
#endif

namespace mrw {

  /** @addtogroup debug */
  //@{
  
  /** @defgroup FunctionTrace Function Tracing (using log4cxx)
      @pre \#include <mrw/functiontrace.hpp>
      
      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 <mrw/functiontrace.hpp>
      \#include <log4cxx/basicconfigurator.h>
      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<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec
             <<std::setw(2+_level)<<std::setfill(' ')<<"\\ "<<_name;
          logger->forcedLog(log4cxx::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), _file(file), _line(line), _tracer(tracer) {
        log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer));
        if (logger->isDebugEnabled()) {
          std::stringstream oss;
          oss<<std::setw(17)<<' '
             <<std::setw(2+_level)<<std::setfill(' ')<<"\\ "<<_name;
          logger->forcedLog(log4cxx::Level::DEBUG, oss.str(),
                            MRW_LOG4CXX_LOCATION);
          ++_level;
        }
      }
      ~FnTrace() throw() {
        log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer));
        if (logger->isDebugEnabled()) {
          --_level;
          std::stringstream oss;
          if (_addr)
            oss<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec;
          else
            oss<<std::setw(17)<<' '; 
          oss<<std::setw(2+_level)<<std::setfill(' ')<<"/ "<<_name;
          logger->forcedLog(log4cxx::Level::DEBUG, oss.str(),
                            MRW_LOG4CXX_LOCATION);
        }
      }
    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
