Sign in to follow this  
Endar

Message Log class

Recommended Posts

Endar    668
Okay, here's what I've got so far:
#ifndef _CLOG_H_
#define _CLOG_H_

#include <string>
#include <iostream>
#include <fstream>
using namespace std;
#include <time.h>

#define DEFAULT_STREAM_FILE "default_stream.txt"
#define TIME_BUFFER_SIZE 100

/**
 * This is a singleton class that will allow objects to output debug and other messages
 */
class CLog
{
private:	
	static ofstream streams[10];	///< Output file stream

public:

	/// Enumeration representing the different message (to different streams)
	enum StreamType{
		STREAM_DEBUG=0,	///< debug stream
		STREAM_2,
		STREAM_3,
		STREAM_4,
		STREAM_5,
		STREAM_6,
		STREAM_7,
		STREAM_8,
		STREAM_9,
		STREAM_10,

		NUM_OF_STREAMS	///< Implicit number of streams
	};
	
	/// Enumeration representing the different write modes
	enum WriteMode{

		WRITE=0,		///< truncates file and starts writing
		APPEND,			///< append to existing file

		NUM_OF_MODES	///< Implicit number of write modes
	};



	/**
	 * Function that opens the specified file for the specified stream in the specified
	 * write mode.
	 * \param file The file name of the file into which will be written the messages logged for this stream.
	 * \param type The stream you want to associate with this file.
	 * \param mode The write mode you wish to open this file in.
	 */
	static void openStream(string file = DEFAULT_STREAM_FILE, StreamType type = STREAM_DEBUG, WriteMode mode = WRITE )
	{
		// Check if it is a valid write mode
		if( mode < WRITE || mode >= NUM_OF_MODES ){
			cout << "CLog::openStream: Error: Invalid WriteMode: not opening file." << endl;
			return;
		}

		if( type < STREAM_DEBUG || type >= NUM_OF_STREAMS ){
			cout << "CLog::openStream: Error: Invalid StreamType: not opening file." << endl;
			return;
		}


		// Open file in mode specified
		if( mode == APPEND )
			// Open file stream in append mode
			streams[(int)type].open(file.c_str(), ios::app);
		else if( mode == WRITE )
			// Open file stream in write mode
			streams[(int)type].open(file.c_str() );

	}


	/**
	 * Flush and close the selected file stream.
	 * \param type The stream to close.
	 */
	static void closeStream(StreamType type)
	{
		streams[(int)type].flush();
		streams[(int)type].close();
	}

	/** 
	 * Function that will write a message to the open specified stream.
	 * \param type A StreamType variable indicating which stream you want to write the message to.
	 * \param s A string object containing the message you want to log.
	 */
	static void write(StreamType type, string s)
	{
		// If file is open then write the string to file
		if( streams[(int)type].is_open() ){
			writeTime(streams[(int)type]);
			streams[(int)type] << s;
		}
		else
			// Else file is not open -> object has not been created
			cout << "CLog::write: Warning: attempted writing to stream " << (int)type <<
					" when stream is not open." << endl;
	}

	/** 
	 * Function that will write the time and date in the form of
	 * day/month/year hour:minute:second into the stream passed.
	 * \param s A reference to the stream to write the date and time into.
	 */
	static void writeTime(ofstream& s)
	{
		// get time and print it to file before message
		time_t t = time(NULL);
		struct tm* now = localtime( &t );
		char buf[TIME_BUFFER_SIZE];
		strftime(buf, TIME_BUFFER_SIZE, "%d/%m/%y %I:%M:%S\t ", now);

		s << buf;
	}

};

/** Out of class scope initialization for the streams */
ofstream CLog::streams[10];

#endif


I just re-wrote it from some wierd singleton mash that seemed to make sense about half-way through last year, fairly close to the start of my C++ experience. I haven't tested it yet, because even for something this small, it would take some time, and well, I feel a little more like sleeping. But I wanted to ask some advice about this class. Nothing specific, anything you felt like giving that might be useful, but, since a question is usually asked, which do you think is better for a message logging class? An actual object? A singleton? Or just a bunch of related functions?

Share this post


Link to post
Share on other sites
nilkn    960
I personally think a singleton is fine, but others may disagree.

Generally, you should check these things before using a singleton:

* Will every portion of the program use the class exactly the same way?

* Will every program using the class use it exactly the same way?

* Will every program never need more than one instance of the class?

* Can clients use the class without knowledge of what application they're in?

If you answer yes to all those, you pretty much OK to use a singleton.

EDIT: Whether you go with a singleton or not, one thing is sure: don't just make a bunch of global functions with 'Log_' prefixed to them. If you're going to do that, the least you can do is put them into a 'Log' namespace or something.

[Edited by - nilkn on July 4, 2005 10:55:12 AM]

Share this post


Link to post
Share on other sites
Telastyn    3777
Personally, I would [and do] use an interface. That is have a pure virtual class which has virtual members for all the stuff you require a logger class to have. [for me it's merely write(const char *)] Then you can make various loggers to see what works best [or change what works best depending on the scenario] and just have your classes write to the base interface. Let inheritance do the rest.

Share this post


Link to post
Share on other sites
okonomiyaki    548
Instead of having many anonymous streams, I have found it very useful and easier just to give a couple pre-built names streams. This way you can control what the output is according to the type of message, etc.

It's not the most flexible, but I don't need it to be any more. I could change how it logs each type of message if I want (right now it logs all of them the same way).

Here it is if you want to take a look.

.h

/* --------------------------------------------------------------
File: AXLogger.h
Description: This file defines a class that logs messages. Depending on the type of message,
it logs it to the appropriate output.
For now, it logs everything to both the console and the file. I think it's important
to separate the types of messages, however, so later I can come back and change the type of
outputs for each type of message.

You specify the type of message by passing in MESSAGE_USER, MESSAGE_APP, or MESSAGE_SYSTEM
when you write the message. A line with the type of message, calling function, and message
will appear in the appropriate output. You are allowed printf style formatting.
Date: June 29, 2005
Author: James Long
-------------------------------------------------------------- */



#if !defined(AXLOGGER_INCLUDE)
#define AXLOGGER_INCLUDE

// The different log types
enum LogType {
LOG_TYPE_USER = 1,
LOG_TYPE_APPLICATION = 2,
LOG_TYPE_SYSTEM = 4,
LOG_TYPE_DEBUG = 8
};

// Define some macros to make logging much easier
#define MESSAGE_USER LOG_TYPE_USER, __FUNCTION__
#define MESSAGE_APP LOG_TYPE_APPLICATION, __FUNCTION__
#define MESSAGE_SYS LOG_TYPE_SYSTEM, __FUNCTION__
#define MESSAGE_DBG LOG_TYPE_DEBUG, __FUNCTION__

class AXLogger : AXSingleton<AXLogger> {
public:
AXLogger();
virtual ~AXLogger();

AXResult Initialize();
void Shutdown();

void Write(LogType target, const char* func, const char* msg, ...);

private:
char _Temp_Buf[5012];
ofstream _FileLog;
};


#endif






.cpp

/* --------------------------------------------------------------
File: AXLogger.cpp
Description: Implementation for the AXLogger class. See AXLogger.h for details.
Date: June 29, 2005
Author: James Long
-------------------------------------------------------------- */


#include "..\\AXCore.h"

AXLogger::AXLogger() : AXSingleton<AXLogger>() {
}
AXLogger::~AXLogger() {
}

AXResult AXLogger::Initialize() {
// Open the log file
_FileLog.open("log.txt");
return AXSUCCESS;
}

void AXLogger::Shutdown() {
// Close the log file
_FileLog.close();
}

void AXLogger::Write(LogType target, const char *FunctionName, const char *Msg, ...) {
va_list args;

va_start(args, Msg);
vsprintf(_Temp_Buf, Msg, args);
va_end(args);

// Detect the log type and log accordingly (for now they basically do the same thing)
if(target & LOG_TYPE_USER) {
printf("\nAX User> *** %s *** \n\n", _Temp_Buf);
_FileLog << "\nAX User> *** " << _Temp_Buf << " *** \n\n";
}

if(target & LOG_TYPE_APPLICATION) {
printf("AX Application (%s)> %s\n", FunctionName, _Temp_Buf);
_FileLog << "AX Application (" << FunctionName << ")> " << _Temp_Buf << "\n";
}

if(target & LOG_TYPE_SYSTEM) {
printf("AX System (%s)> %s\n", FunctionName, _Temp_Buf);
_FileLog << "AX System (" << FunctionName << ")> " << _Temp_Buf << "\n";
}

if(target & LOG_TYPE_DEBUG) {
#ifdef _DEBUG
printf("*** AX Debug (%s)> %s\n", FunctionName, _Temp_Buf);
#endif
}

}



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this