Overview
Logging debug information from your code is a great way to find the problems in your application; this is specially usefull when the software is running on some customer's machine on the other side of the earth and you can not be there to investigate the particular conditions in which the application is crashing. The easy scenario described by a customer would sound like: "I choosed the Print preview option from the File menu and the application crashed"; if you can re-produce the bug on your machine, life is not that bad. What if the application is crashing or malfunctioning (apparently) random? How about a process that runs in background (like a Win service)? All you'll hear from the customer will be that he rebooted the machine and after a few minutes a message box showed saying something like "Access violation" or so...
Problem
The logs should look like this:
10/11/2002 15:25:28 CFooClass::FooMethod:
x is greater then zero; x = 5
Besides the string that depends on your application logic ("x is greater the zero; x = 5"), the log contains information about when (10/11/2002 15:25:28) and where (CFooClass::FooMethod) the log was made. The log can also contain the file and the line number where it was made:
10/11/2002 15:25:28 File: "C:\Adi\.NET\test1\test1.cpp";
CFooClass::FooMethod; Line: 16: x is greater the zero; x = 5
The time stamp can be easily automated; automating the log of the other information requires the use of some predefined macros.
Solution
__FILE__
and __LINE__
are ANSI predefined macros (which Microsoft's compiler implements as well); this macros are replaced with the file name and the line number where these appear in source file. Besides these, Visual C++ 7.0 compiler introduces some extra predefined macros.
__FUNCDNAME__
returns the decorated name of the enclosing function (the C++ mangled function name)
__FUNCSIG__
is replaced with the signature of the enclosing function.
__FUNCTION__
is replaced with name of the enclosing function.
Note: These macros can only be used within a function. The function name includes class and namespace. Using these macros, we can automate the output of the required information.
The class CSmartLogger
is similar to the basic_ostream
from STL. The operator << is defined for most of the standard C++ types. A purpose of this class was to be used in the following way:
CSmartLogger smartlogger;
void CFooClass::FooMethod(int x)
{
if (x > 0)
smartlog << "x is greater the zero; x = " << x;
else
smartlog << "x is NOT greater the zero; x = " << x;
}
and the result to be:
10/11/2002 15:25:28 File: "C:\Adi\.NET\test1\test1.cpp";
CFooClass::FooMethod; Line: 16: x is greater the zero; x = 5
As you can see, the "header" of a log can not be written when every << operator is used (using two or more operators would multiply the header of the log); even more we can not use the code that runs in << operator because we are using macros that depend on the source file and line number where they appear. Instead we can define another macro which will be inlined by the preprocessor:
extern CSmartLog __smartlog;
#define smartlog __smartlog.WriteFlags(__FILE__,
__LINE__, __FUNCTION__, __FUNCSIG__)
The problem with this solution is that using smartlog
would log the header information, even if the intension was to set a property of class CSmartLoger
; for this purpose you should use __smartlog
.
To make the log class more customized, we use another class that contains the flag for the information to be logged
class CLogFlags
{
public:
CLogFlags();
bool m_bGlobalFlag;
bool m_bFileFlag;
bool m_bLineFlag;
bool m_bFunctionFlag;
bool m_bFunctionSignFlag;
bool m_bTimestampFlag;
};