Archived

This topic is now archived and is closed to further replies.

MisterMoot

Logger class that only logs in Debug build

Recommended Posts

I recently changed the design of my class that logs messages to file, but with the ability to only log some messages if I'm building the project in Debug configuration. For the Release build I want these messages skipped completely and not included in the final code. Originally I had a globally declared instance of my logger class. I'd then include a common header file in any classes that needed to do any logging. This would have a macro conditionally defined to call the logger print method in Debug build, and do nothing in Release build, something like this...
// Common.h


extern CLogger MainLog;

#ifdef _DEBUG
	#define DEBUG_LOG MainLog.Print
#else
	#define DEBUG_LOG void
#endif

// Some .cpp file that needs to use logger


#include "Common.h"

DEBUG_LOG("Log this message to file");
This approach worked, but I didn't like using the global, so I redesigned the logger so that any component that needs to log messages just has to create an instance of the logger class, with multiple instances of the class able to share a common log file or create their own. I'm very happy with the new class and I can now get rid of the common header file and global completely. However, I'm no longer able to conditionally define a debug logging macro, as each instance of the class has a different name. I've therefore decided to use code similar to the test code shown below. This works exactly as I want it to. In Debug builds, call to PrintIfDebug generate code and call the Print() method, whereas in Release builds no code is generated at all for the PrintIfDebug() line.
   
class DebugTest
{

public:

	void Print(int Msg) {cout << Msg << endl;};

#ifdef _DEBUG
	void PrintIfDebug(int Msg) {Print(Msg);}	// Debug build, so call the print method

#else
	void PrintIfDebug(int Msg) {void(0);}	 // Release build, so do nothing

#endif 

};
The questions I have are: 1. Is no code generated in Release build because the compiler has realised that PrintIfDebug does nothing and so optimised it out, and if so is this standard for all compilers? 2. What do you think of this approach? It obviously relies on the logger class itself being recompiled for release versions. However, the only alternative I can think of is having a conditionally compiled macro for every instance of the class (see below). Is this a better approach?
//   

// My DirectInput class .cpp file needs logging, so...

//


#include "CLogger.h"

CLogger DInputLog();

#ifdef _DEBUG
	#define DEBUG_LOG DInputLog.Print
#else
	#define DEBUG_LOG void
#endif

//

// My D3D class .cpp file needs logging, so...

//


#include "CLogger.h"

CLogger D3DLog();

#ifdef _DEBUG
	#define DEBUG_LOG D3DLog.Print
#else
	#define DEBUG_LOG void
#endif

//

// and I have to do this for each CLogger instance

Sorry for such a long post. All comments and ideas are welcome. [edited by - MisterMoot on July 25, 2003 11:04:30 PM]

Share this post


Link to post
Share on other sites
Macros are, in most cases, evil, and this is a good example. Your conditional compilation example is definitely workable, but consider doing it this way:


class DebugTest
{
public:
void Print(int Msg) {cout << Msg << endl;};
void PrintIfDebug(int Msg)
{
#ifdef _DEBUG
Print(Msg);
#else
Msg; // is unused; this avoids an "arg not referenced" warning

#endif
}
};


[edited by - sneftel on July 25, 2003 11:52:49 PM]

Share this post


Link to post
Share on other sites
Wouldn''t something like this be a lot easier...

//--

//--Log.h

//--

class CLogger
{
public:
CLogger();
~CLogger();
//--

//--Just for the example

//--

void OpenLog(char* Filename);
void Print(int Msg);
void CloseLog(void);
};


//--

//--Log.cpp

//--

CLogger::CLogger()
{
//--Process

}
//

CLogger::~CLogger()
{
//--Process

}
//

#ifndef _DEBUG
void CLogger::OpenLog(char* Filename)
{
//--Process

}
//

void CLogger::Print(int Msg)
{
//--Process

}
//

void CLogger::CloseLog(void)
{
//--Process

}
#else
void CLogger::OpenLog(char* Filename){return;}
void CLogger::Print(int Msg){return;}
void CLogger::CloseLog(void){return;}
#endif


Then you can use it like a normal class and not have to worry about debug or release.

-UltimaX-

"You wished for a white christmas... Now go shovel your wishes!"

Share this post


Link to post
Share on other sites
Thanks, Sneftel, that''s a much tidier way of doing it. I''ll change my code accordingly.

UltimaX, I still need most of the class functionality to work in the release build. I''ll still be logging some information. It''s just certain information that I only want to log in the debug build.

Thanks to you both for taking the time to read and reply.

Share this post


Link to post
Share on other sites