In this tutorial I will show you now how to implement a logger class.
If you don't know yet, a logger class is used to record some key actions from the game engine.
This actions (time and place at witch the action occured, a text describing the action, the value of a variable etc)
will be recorded in a file (a text file, an html file etc).
The logger can print failures, successes, information’s or any other info you want.
It is extremly usefull because unlike the asserts it can be active in release mode also.
The implementation of this class I will show you does the following things:
- prints the information in a html file (you can print the information in a text file, but I choose a html file because in it you can use colors to delimitate the problems for the normal actions);
- Opens and closes the file every time it prints in it (it is slower, but the info isn’t lost if the game crashes).
- has 5 types of colors from diffrent types of actions: success (green), failure (dark grey), critical failure (red), warning (yellow), information (blue);
- if critical failure or failure, the logger can print an assert or the information to the output window; this action can be disabled with a function call;
- the logger can be disabled with a function call;
- has a functions that returns the date and time and it's printed every time an action is recorded
///////////////////////////// CLOGGER.H FILE ///////////////////////////////
#ifndef _CLOGGER_H_
#define _CLOGGER_H_
#include <windows.h>
#include <stdio.h>
// The declaration of the logger class
class CLogger
{
public:
// The enums that define the type of each message
enum m_eColors
{
eSUCCESS = 0, // action ended successfully (ex: a image is loaded)
eFAILURE, // an action has failed (ex: an image has not loaded)
eCRITICAL_FAILURE, // an actions has failed and is a great change that the engine will crashes (ex: an ininitialized pointer)
eINFORMATION, // an information (ex: the infromation of the system (CPU, RAM etc))
eWARNING // an action has occurred that may lead to problems (ex: a divide by 0)
};
// Default Constructor. Does nothing
CLogger() { m_bClear = false; };
// Default Destructor. Clears the logger if a variable will indicate that
~CLogger();
// Initializes the logger in a file (creates or opens it). szFilename is the file name when the logger will write the info.
// The function will return true if successful and false otherwise
bool bInit(char* szFilename);
// Prints a message into the logger file into a form similar with printf
// Example: (message_type, "file_name - function_name: message %d!\n, integer_variable");
// eColor is the type of the message
// szText is the message to be written
void LogPrint(m_eColors eColor, const char *szText, ...);
// Enables the logger
inline void Enable(void) { m_bEnabled = true; };
// Disables the logger
inline void Disable(void) { m_bEnabled = false; };
// Returns true if the logger is enabled
inline bool bIsEnable(void) const { return m_bEnabled; };
// Enables the logger to lunch asserts when failure and critical failure occur
inline void EnableAsserts(void) { m_bUseAssert = true; };
// Disables the logger to lunch asserts when failure and critical failure occur
inline void DisableAsserts(void) { m_bUseAssert = false; };
// @return true if the logger lunches asserts on eFAILURE and eCRITICAL_FAILURE, false otherwise
inline bool bIsAssertEnable(void) const { return m_bUseAssert; };
// Clears the messages in the logger file (the logger file will be empty)
void Empty(void);
// Closes the logger and before that prints a message
void DeInit();
// Gets and converts the last error (given by GetLastError)
// returns the last error (given by GetLastError) converted
LPTSTR szGetError(void) const;
// Converts the last error (given by GetLastError)
// uError is the windows error
// returns the last error (given by GetLastError) converted
LPTSTR szGetError(const unsigned long& uError) const;
private:
// Converts the time and date in the format "dd-mm-yyyy, hh:mm:ss"
void GetTime();
// The name of the file
char* m_szFilename;
// The file pointer
FILE *m_pFile;
// True if the logger is enabled, false otherwise
bool m_bEnabled;
// True if we put an assert on eFAILURE and eCRITICAL_FAILURE
bool m_bUseAssert;
// True if we will clear the logger
bool m_bClear;
// Buffer to hold the text
char m_szBuffer[2048];
// Buffer that holds the time
char m_szTimeBuffer[256];
// Gets the color specific to eColor
// eColor is the message type
// return the color specific with the given message type
char* pcGetColor(m_eColors eColor);
};
#endif
///////////////////////////// CLOGGER.CPP FILE ///////////////////////////////
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include "CLogger.h"
// Default Destructor. Clears the logger if a variable will indicate that
CLogger::~CLogger()
{
if (m_bClear) // If this variable is true...
Empty(); // We will clear the logger
}
// Initializes the logger in a file (creates or opens it). szFilename is the file name when the logger will write the info.
// The function will return true if successful and false otherwise
bool CLogger::bInit(char* szFilename)
{
m_szFilename = szFilename;
if (m_szFilename == NULL)
return false;
// Creates a file in the *.exe directory called szFile.log
fopen_s(&m_pFile, m_szFilename, "at");
// If an error occurs returns false
if (m_pFile == 0)
return false;
// Gets the date and time
GetTime();
// Setup the HTML file
fprintf(m_pFile, "<HTML>\n<TITLE>%s</TITLE>\n"
"<BODY BGCOLOR = 000000>\n"
"<FONT COLOR= 00CCFF>%s</FONT><BR><BR>\n"
"</BODY>\n</HTML>", m_szTimeBuffer, m_szTimeBuffer);
// Prints a message telling that the logger was started
fprintf(m_pFile, "<FONT COLOR= \"%s\">%s: *************************************************************\</PRE></FONT>\n" "</BODY>\n</HTML>\n",
pcGetColor(eINFORMATION), m_szTimeBuffer);
fprintf(m_pFile, "<FONT COLOR= \"%s\">%s: *** Logger Started ***</PRE></FONT>\n" "</BODY>\n</HTML>\n\n",
pcGetColor(eINFORMATION), m_szTimeBuffer);
m_bEnabled = true; // The logger is enabled
m_bUseAssert = true; // Enables the asserts (if error or critical error a assert will be displayed)
fclose(m_pFile); // closes the file... we will open and close it every time we write to the logger to avoid losing data in a crash
return true;
}
// Prints a message into the logger file into a form similar with printf
// Example: (message_type, "file_name - function_name: message %d!\n, integer_variable");
// eColor is the type of the message
// szText is the message to be written
void CLogger::LogPrint(m_eColors eColor, const char *szText, ...)
{
if (!m_bEnabled) // The logger isn't enabled so we don't print anything
return;
va_list args;
va_start(args, szText);
vsprintf_s(m_szTimeBuffer, sizeof(m_szTimeBuffer), szText, args); // Copy the text to buffer
va_end(args);
fopen_s(&m_pFile, m_szFilename, "a+t");
// Prints the text with a color and a date and time
GetTime();
fprintf(m_pFile, "<FONT COLOR= \"%s\">%s: %s</PRE></FONT>\n"
"</BODY>\n</HTML>", pcGetColor(eColor), m_szTimeBuffer, m_szTimeBuffer);
// If we use asserts and a critical failure has occurd we print to the IDE output string and we call an assert
if ((eColor == eFAILURE || eColor == eCRITICAL_FAILURE) && m_bUseAssert)
{
OutputDebugString(m_szTimeBuffer);
assert(!"Critical error occurred!");
}
fclose(m_pFile);
}
// Clears the messages in the logger file (the logger file will be empty)
void CLogger::Empty(void)
{
fopen_s(&m_pFile, m_szFilename, "w");
fclose(m_pFile);
}
// Closes the logger and before that prints a message
void CLogger::DeInit()
{
fopen_s(&m_pFile, m_szFilename, "a+t");
GetTime();
fprintf(m_pFile, "\n<FONT COLOR= \"%s\">%s: *** Logger Stopped ***</PRE></FONT>\n" "</BODY>\n</HTML>\n",
pcGetColor(eINFORMATION), m_szTimeBuffer);
fprintf(m_pFile, "<FONT COLOR= \"%s\">%s: *************************************************************</PRE></FONT>\n" "</BODY>\n</HTML>\n\n\n",
pcGetColor(eINFORMATION), m_szTimeBuffer);
fclose(m_pFile);
}
// Gets and converts the last error (given by GetLastError)
// returns the last error (given by GetLastError) converted
LPTSTR CLogger::szGetError(void) const
{
LPTSTR lpMsgBuf = NULL;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
return lpMsgBuf;
}
// Converts the last error (given by GetLastError)
// uError is the windows error
// returns the last error (given by GetLastError) converted
LPTSTR CLogger::szGetError(const unsigned long& uError) const
{
LPTSTR lpMsgBuf = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, uError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
return lpMsgBuf;
}
// Gets the color specific to eColor
// eColor is the message type
// return the color specific with the given message type
char* CLogger::pcGetColor(m_eColors eColor)
{
switch (eColor)
{
case eSUCCESS:
return "#66FF00"; // Green
case eFAILURE:
m_bClear = true;
return "#666666"; // Dark Grey
case eCRITICAL_FAILURE:
m_bClear = true;
return "#FF0000"; // Red
case eWARNING:
m_bClear = true;
return "666600"; // Yellow
case eINFORMATION:
return "#00CCFF"; // Blue
}
return NULL;
}
// Converts the time and date in the format "dd-mm-yyyy, hh:mm:ss"
void CLogger::GetTime()
{
time_t t;
memset(m_szTimeBuffer, NULL, sizeof(m_szTimeBuffer));
time(&t);
char *cpTime = ctime(&t);
// Day
m_szTimeBuffer[0] = cpTime[8];
m_szTimeBuffer[1] = cpTime[9];
m_szTimeBuffer[2] = '-';
// Month
m_szTimeBuffer[3] = cpTime[4];
m_szTimeBuffer[4] = cpTime[5];
m_szTimeBuffer[5] = cpTime[6];
m_szTimeBuffer[6] = '-';
// Year
m_szTimeBuffer[7] = cpTime[20];
m_szTimeBuffer[8] = cpTime[21];
m_szTimeBuffer[9] = cpTime[22];
m_szTimeBuffer[10] = cpTime[23];
m_szTimeBuffer[11] = ':';
m_szTimeBuffer[12] = ' ';
// Hour
m_szTimeBuffer[13] = cpTime[11];
m_szTimeBuffer[14] = cpTime[12];
m_szTimeBuffer[15] = cpTime[13];
m_szTimeBuffer[16] = cpTime[14];
m_szTimeBuffer[17] = cpTime[15];
m_szTimeBuffer[18] = cpTime[16];
m_szTimeBuffer[19] = cpTime[17];
m_szTimeBuffer[20] = cpTime[18];
m_szTimeBuffer[21] = '\0';
}
So we reach the end of this tutorial. I hope you enjoy reading it and that you learned something from it.
I have some suggestions for improving this logger:
- add a system to log the system information (CPU, RAM, Videocard, extensions etc)
- add a system to help you get the errors (from OpenGL, DirectX etc)
- add a singleton to allow only a single instance of the logger
Well If you have any questions, suggestions, you found some mistakes in this tutorial or you know a better way to do some thing,
please mail me at liviuemanuel@yahoo.com