/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ 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 <log4cxx/logger.h>
# include <sstream>
# include <iomanip>
# include <string>
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 ( ) ,
_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 < < std : : setw ( 17 ) < < ' '
< < std : : setw ( 2 + _level ) < < std : : setfill ( ' ' ) < < " \\ " < < _name ;
logger - > 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 < < 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 ( ) ,
_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