Recently I have been on the lookout for a relatively easy to use logging feature for my C++ applications. There are a lot of good frameworks out there. I wasn't so much as looking for using or even copy pasting available code than I was looking for approaches to logging. Then I went through Log 4 C++. It's a nice framework. This logging class which I present here is based on ideas from Log 4 C++ in that the usage pattern is similar to Log 4 C++. Most logging frameworks provide a plethora of tweaks and formatting options which this little class here does not, and this is precisely why I find using it very easy. The entire code fits into one single .h file and is easily understandable and can therefore be easily modified to suit one's needs. Please note, the code uses some of the C++ 11 features. However, it should be relatively easy to get the code to work with the older specification; for example replace 'enum class' with just 'enum'. Visit http://en.wikipedia.org/wiki/C%2B%2B11 for more information. The sample code compiled in Visual Studio 2012.
CLogger
framework:: Diagnostics
framework::Threading
CIntraProcessLock
CNoLock
Lock()
Unlock()
ostream
WRITELOG
Creating the logger. E.g.:
using namespace framework::Diagnostics; using namespace framework::Threading; CLogger<CNolock> logger(LogLevel::Info, _T("MyApp"));
The constructor has the following parameters:
LogLevel
framework::Diagnostics::LogLevel
Name
logItems
framework::Diagnostics::LogItem
Suppose you want to log only the date-time and the actual log statement, you would create a logger object like:
CLogger<CNoLock> logger(LogLevel::Info, _T("MyApp"), static_cast<int>(LogItem::DateTime));
Note, there is no way to prevent actual log statements from being written so there is no LogItem for it.
LogItem
The next step is to add the output streams. For each output stream that you add, you can specify the logging-level. For example, we can add 'cout' and specify a logging-level of Error. This would mean that the level-specified and all levels higher than this will be logged.
cout
Error
Similarly, if one adds a file stream and specifies Info as the log-level, all levels above Info and Info itself will be logged, which in effect means log everything since all other levels are higher than Info (see the enum LogLevel). E.g.:
Info
logger.AddOutputStream(std::wcout, false, LogLevel::Error); logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), true, framework::Diagnostics::LogLevel::Info);
The addOutputStream has the following parameters:
addOutputStream
os
wcout
own
filestream
WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting")); WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T("Something may have gone wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program Ending"));
The entire code in one place:
#include "threading.h" #include "Logger.h" #include <fstream> using namespace framework::Diagnostics; using namespace framework::Threading; int _tmain(int argc, _TCHAR* argv[]) { CLogger<CNolock> logger(LogLevel::Info, _T("MyApp")); logger.AddOutputStream(std::wcout, false, LogLevel::Error); logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), true, framework::Diagnostics::LogLevel::Info); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting")); WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T("Something may have gone wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program Ending")); return 0; }
The use of templated classes for performing thread synchronization is based on the concept of policy-classes (refer to 'Modern C++ Design' by 'Andrei Alexander). Log 4 C++ inspired the overall design of this class.
Thanks to my wife Meena for being there always and my son Neel for showing how simple it is to have a happy life.