I personally prefer runtime enabled logging. That means that you don't have to recompile to get the "debug output". So I have a command-line argument -v=n
, where n
defaults to zero, and gets stored in the variable verbosity
in the actual program. With -v=1
I get basic tracing (basic flow of the code), with -v=2
I get "more stuff" (such as dumps of internal state in selected functions). Two levels is often enough, but three levels may be good at times. An alternative is to make verbosity
a bit-pattern, and enable/disable certain functionality based on which bits are set - so set bit 0
for basic trace, bit 1
gives extra info in some module, bit 2 gives extra trace in another module, etc. If you want to be REALLY fancy, you have names, such as -trace=all_basic
, -trace=detailed
, -trace=module_A
, -trace=all_basic,module_A,module_B
or some such.
Combine this with a macro along the lines of:
#define TRACE do { if (verbosity > 0) \
std::cout << __FILE__ << ":" << __LINE__ << ":" \
<< __PRETTY_FUNCTION__ << std::endl; } while(0)
For things that may take a substantial amount of extra time, such as verifying the correctness of a large and complex data structure (tree, linked list, etc), then using #ifndef NDEBUG
around that code would be a good thing. Assuming of course you believe that you'll never mess that up in a release build.
Real livig code here:
https://github.com/Leporacanthicus/lacsap/blob/master/trace.h
https://github.com/Leporacanthicus/lacsap/blob/master/trace.cpp
being use here for example:
https://github.com/Leporacanthicus/lacsap/blob/master/expr.cpp
(Note that some simple functions that get called a lot don't have "TRACE" - it just clutters up the trace and makes it far too long)