/** @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
#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
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(),
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<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<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