Click here to Skip to main content
15,878,809 members
Articles / Programming Languages / C++

Simple Logger for C++

Rate me:
Please Sign up or sign in to vote.
4.88/5 (33 votes)
6 Jun 2015CPOL4 min read 116.3K   4.7K   70   34
A simple and extensible logger for C++

Overview 

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.

Design   

The main class which we need to be concerned about is the CLogger class. It is present in the 'framework:: Diagnostics' namespace. CLogger takes the help of another class for thread-synchronization functions. As of now, there are two classes which help with this and both are present under the framework::Threading namespace. One of them is called CIntraProcessLock. Use this if you are sharing an instance of CLogger across multiple threads in the same process. If you are not sharing a logger object across threads, use the CNoLock class. When creating an instance of the logger, you need to supply at least one of these or any other class which provides semantically correct Lock() and Unlock() functionality. Once the CLogger instance is created, we need to add ostream derived objects which perform the actual task of writing something somewhere. Finally to log something, use the WRITELOG macro.

Usage 

Creating the logger. Example:

C++
using namespace framework::Diagnostics;
using namespace framework::Threading;
CLogger<CNolock> logger(LogLevel::Info, _T("MyApp"));

The constructor has the following parameters:

  • LogLevel - can be one of the enum values of framework::Diagnostics::LogLevel enumeration. This value affects what gets logged. This value is used when you add an output stream.
  • Name - A name given to a logger object. This name can appear in each log line. I usually have one logger object for a module and give the name of the module here.  
  • logItems - This is a bitmask of the list of items we want to output to each log line. The values should be taken from the framework::Diagnostics::LogItem enumeration. By default, the following items are logged:
Log Item Default Selected 
Filename No
LineNumber Yes
Function Name Yes
DateTime Yes
ThreadId No
LoggerName Yes
LogLevel Yes

Suppose you want to log only the date-time and the actual log statement, you would create a logger object like:

C++
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.

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.

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). Example:

C++
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:

  • os - pass any ostream derived class, e.g., cout or wcout.
  • own - if true, the CLogger instance will use the delete operator to delete the passed in ostream object. This is usually useful if you are creating a new instance of a filestream and passing it in (see the code above).
  • LogLevel - all log entries equal to or higher will be written for this device (output stream). If nothing is specified, the logging level specified on the CLogger object will be used to decide what gets logged. Finally, to perform the logging itself, it is recommended that one use the WRITELOG, LOGINFO, LOGDEBUG, LOGWARN or LOGERROR macro. Example:
C++
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"));

// Or use shortcut macros like LOGINFO, LOGDEBUG, LOGWARN, LOGERROR. E.g.
LOGINFO(logger, _T("Program Ending"));

// If you have a pointer to a logger object e.g:
CLogger<cnolock>* loggerPtr = &logger; 
LOGINFOP(loggerPtr, _T("Program Ending"));

</cnolock>

In case you have a pointer to a logger object, you can use the same macros suffixed with 'P' (P for pointer) which are WRITELOGP, LOGINFOP, LOGDEBUGP, LOGWARNP or LOGERRORP

The entire code is in one place:

C++
#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"));
    CLogger<CNoLock>* loggerPtr = &logger; // An easy way to use pointer to a logger object
	LOGINFOP(loggerPtr, _T("Program Ending"));
    return 0;
}

 Image 1

 Image 2

Thanks   

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 always being there and my son Neel for showing how simple it is to have a happy life.

History

  • 6th June, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
My personal website is at http://sbytestream.pythonanywhere.com

Comments and Discussions

 
Questionlogging to a file Pin
Member 1366996619-Jun-18 7:25
Member 1366996619-Jun-18 7:25 
QuestionHi Siddharth Pin
Member 115857399-Nov-17 16:33
Member 115857399-Nov-17 16:33 
QuestionHow write a string into log Pin
Frank Pez3-Jul-17 23:55
Frank Pez3-Jul-17 23:55 
AnswerRe: How write a string into log Pin
Frank Pez11-Jul-17 23:33
Frank Pez11-Jul-17 23:33 
QuestionNice article and some compiler errors Pin
Md Saleem Navalur6-May-16 1:51
Md Saleem Navalur6-May-16 1:51 
AnswerRe: Nice article and some compiler errors Pin
Siddharth R Barman30-May-16 3:47
Siddharth R Barman30-May-16 3:47 
QuestionUnicode support Pin
Member 192040429-Dec-15 3:16
Member 192040429-Dec-15 3:16 
AnswerRe: Unicode support Pin
Siddharth R Barman30-May-16 4:05
Siddharth R Barman30-May-16 4:05 
QuestionChinese cannot be writern Pin
hspbh8-Jun-15 0:19
hspbh8-Jun-15 0:19 
AnswerRe: Chinese cannot be writern Pin
Siddharth R Barman30-May-16 4:02
Siddharth R Barman30-May-16 4:02 
GeneralRe: Chinese cannot be writern Pin
hspbh6-Jul-16 17:30
hspbh6-Jul-16 17:30 
NewsExisting logger framework Pin
TheHumbleGuy7-Jun-15 21:33
TheHumbleGuy7-Jun-15 21:33 
NewsRe: Existing logger framework Pin
Sergey Podobry8-Jun-15 0:28
professionalSergey Podobry8-Jun-15 0:28 
QuestionVery helpful utility Pin
Member 1044647720-May-15 5:37
Member 1044647720-May-15 5:37 
AnswerRe: Very helpful utility Pin
Siddharth R Barman6-Jun-15 15:05
Siddharth R Barman6-Jun-15 15:05 
QuestionVS2008? Pin
charlieg30-Oct-14 12:34
charlieg30-Oct-14 12:34 
AnswerRe: VS2008? Pin
charlieg1-Nov-14 11:18
charlieg1-Nov-14 11:18 
GeneralRe: VS2008? Pin
Siddharth R Barman22-Feb-15 6:08
Siddharth R Barman22-Feb-15 6:08 
GeneralRe: VS2008? Pin
charlieg22-Feb-15 6:32
charlieg22-Feb-15 6:32 
Suggestionsource code update Pin
Roman Minyaylov17-Oct-14 6:42
Roman Minyaylov17-Oct-14 6:42 
Questionthread-synchronization Pin
Member 1107965814-Sep-14 4:25
Member 1107965814-Sep-14 4:25 
AnswerRe: thread-synchronization Pin
Siddharth R Barman18-Apr-15 19:00
Siddharth R Barman18-Apr-15 19:00 
GeneralMy vote of 5 Pin
Manikandan101-Jul-14 5:21
professionalManikandan101-Jul-14 5:21 
QuestionLog limit Pin
vidur sharam30-Jun-14 23:00
vidur sharam30-Jun-14 23:00 
Questionmultiple logger instances logging into a single ostream Pin
visansi8-Jul-13 6:54
visansi8-Jul-13 6:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.