This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Log File Class Submitted by

Here's a COTD contribution... it's a class that handles log files with indentation, code flow, and other stuff. It's also my first use of the STL, so I hope I don't get hammered too badly... :)

Currently browsing [cotd-logger.zip] (6,313 bytes) - [logger.h] - (6,507 bytes)

    // --------------------------------------------------------------------------------------------------------------------------------
    // Copyright 2000, Paul Nettle. All rights reserved.
    //
    // Logger.h - Log file class
    //
    // [NOTE] This file is best viewed in 132 column mode with 8-character tabs
    //
    // THIS FILE HAS BEEN ENTERED INTO THE PUBLIC DOMAIN BY THE AUTHOR
    // --------------------------------------------------------------------------------------------------------------------------------

    #ifndef	_H_LOGGER
    #define	_H_LOGGER

    // --------------------------------------------------------------------------------------------------------------------------------
    // Required includes
    // --------------------------------------------------------------------------------------------------------------------------------

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

    // --------------------------------------------------------------------------------------------------------------------------------
    // The global logger
    // --------------------------------------------------------------------------------------------------------------------------------

    class	Logger;
    extern	Logger	logger;

    // --------------------------------------------------------------------------------------------------------------------------------
    // Macros (necessary evil to take advantage of __LINE__ and __FILE__)
    // --------------------------------------------------------------------------------------------------------------------------------

    #define	LOG		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__,logger.logTex
    #define	HEX		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__,logger.logHex
    #define	RAW		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__,logger.logRaw
    #define	INDENT		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__,logger.indent
    #define	UNDENT		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__,logger.undent
    #define	LOGBLOCK	logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__;LogBlock __lb__

    // If you compiler supports __FUNCTION__, then replace the "#if 1" with "#if 0". Note that this will change the usage of the macro

    #if 1
    #define	LOGFUNC		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__;LogFlow __lf__
    #else
    #define	LOGFUNC		logger.sourceLine() = __LINE__, logger.sourceFile() = __FILE__;LogFlow __lf__(__FUNCTION__)
    #endif

    // --------------------------------------------------------------------------------------------------------------------------------
    // The logger class: does the actual logging
    // --------------------------------------------------------------------------------------------------------------------------------

    class	Logger
    {
    public:
    	// Enumerations

    	enum	LogFlags
    	{
    				LOG_INDENT = 0x00000001,
    				LOG_UNDENT = 0x00000002,
    				LOG_FLOW   = 0x00000004,
    				LOG_BLOK   = 0x00000008,
    				LOG_DATA   = 0x00000010,
    				LOG_INFO   = 0x00000012,
    				LOG_WARN   = 0x00000014,
    				LOG_ERR    = 0x00000018,
    				LOG_CRIT   = 0x00000020,
    				LOG_ALL    = 0xFFFFFFFF
    	};

    	// Construction/Destruction

    inline				Logger(const string &filename)
    				: _logFile(filename), _sourceLine(0), _indentCount(0), _indentChars(4), _logMask(LOG_ALL),
    				  _logStarted(false), _lineCharsFlag(false)
    				{
    					start();
    				}

    inline				~Logger()
    				{
    					stop();
    				}

    	// Operators

    inline		void		operator +=(const string &s)	{logTex(s);}

    	// Accessors

    inline	const	bool		&lineCharsFlag() const	{return _lineCharsFlag;}
    inline		bool		&lineCharsFlag() 	{return _lineCharsFlag;}

    inline	const	unsigned int	&logMask() const	{return _logMask;}
    inline		unsigned int	&logMask()		{return _logMask;}

    inline	const	string		&logFile() const	{return _logFile;}
    inline		string		&logFile()		{return _logFile;}

    inline	const	unsigned int	&sourceLine() const	{return _sourceLine;}
    inline		unsigned int	&sourceLine()		{return _sourceLine;}

    inline	const	string		&sourceFile() const	{return _sourceFile;}
    inline		string		&sourceFile()		{return _sourceFile;}

    inline		bool		logStarted() const	{return _logStarted;}

    	// Utilitarian (public)

    virtual		void		start();
    virtual		void		stop();
    virtual		void		logTex(const string &s, const LogFlags logBits = LOG_INFO);
    virtual		void		logRaw(const string &s);
    virtual		void		logHex(const char *buffer, const unsigned int count, const LogFlags logBits = LOG_INFO);
    virtual		void		indent(const string &s, const LogFlags logBits = LOG_INDENT);
    virtual		void		undent(const string &s, const LogFlags logBits = LOG_UNDENT);

    private:
    	// Utilitarian (private)

    virtual	const	string		&headerString(const LogFlags logBits) const;

    	// Data

    		string		_logFile;
    		string		_sourceFile;
    		unsigned int	_sourceLine;
    		int		_indentCount;
    		int		_indentChars;
    		unsigned int	_logMask;
    		bool		_logStarted;
    		bool		_lineCharsFlag;
    };

    // ---------------------------------------------------------------------------------------------------------------------------------
    // The LogBlock class: used for automatic indentation
    // ---------------------------------------------------------------------------------------------------------------------------------

    class	LogBlock
    {
    public:
    inline				LogBlock(const string &s)	{str = s;logger.indent("Begin block: " + str, Logger::LOG_INDENT);}
    inline				~LogBlock()			{logger.undent("End block: " + str, Logger::LOG_UNDENT);}
    private:
    		string		str;
    };

    // ---------------------------------------------------------------------------------------------------------------------------------
    // The LogFlow class: used for logging code flow
    // ---------------------------------------------------------------------------------------------------------------------------------

    class	LogFlow
    {
    public:
    inline				LogFlow(const char *function)	{str = function;logger.indent("Enter function: " + str, Logger::LOG_FLOW);}
    inline				~LogFlow()			{logger.undent("Exit function: " + str, Logger::LOG_FLOW);}
    private:
    		string		str;
    };

    #endif // _H_LOGGER
    // ---------------------------------------------------------------------------------------------------------------------------------
    // Logger.h - End of file
    // ---------------------------------------------------------------------------------------------------------------------------------

Currently browsing [cotd-logger.zip] (6,313 bytes) - [logger.cpp] - (7,497 bytes)

    // --------------------------------------------------------------------------------------------------------------------------------
    // Copyright 2000, Paul Nettle. All rights reserved.
    //
    // Logger.cpp - Log file class
    //
    // [NOTE] This file is best viewed in 132 column mode with 8-character tabs
    //
    // THIS FILE HAS BEEN ENTERED INTO THE PUBLIC DOMAIN BY THE AUTHOR
    // --------------------------------------------------------------------------------------------------------------------------------

    #include <time.h>
    #include <stdarg.h>

    #include "Logger.h"

    // --------------------------------------------------------------------------------------------------------------------------------
    // The global logger
    // --------------------------------------------------------------------------------------------------------------------------------

    Logger	logger("app.log");

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::start()
    {
    	if (logStarted()) return;
    	_logStarted = true;

    	// Get the time

    	time_t	t = time(NULL);
    	string	ts = asctime(localtime(&t));
    	ts[ts.length() - 1] = 0;

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (of.is_open())
    	{
    		of << "---------------------------------------------- Log begins on " << ts << " ----------------------------------------------" << endl;
    	}
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::stop()
    {
    	// Automatic One time only startup

    	if (!logStarted()) return;

    	// Get the time

    	time_t	t = time(NULL);
    	string	ts = asctime(localtime(&t));
    	ts[ts.length() - 1] = 0;

    	// Start the log

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (of.is_open())
    	{
    		of << "----------------------------------------------- Log ends on " << ts << " -----------------------------------------------" << endl << endl << endl << endl << endl;
    	}

    	_logStarted = false;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::logTex(const string &s, const LogFlags logBits)
    {
    	// If the bits don't match the mask, then bail

    	if (!(logBits & logMask())) return;

    	// Open the file

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (!of.is_open()) return;

    	// Output to the log file

    	of << headerString(logBits) << s << endl;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::logRaw(const string &s)
    {
    	// Open the file

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (!of.is_open()) return;

    	// Log the output

    	of << s;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::logHex(const char *buffer, const unsigned int count, const LogFlags logBits)
    {
    	// No input? No output

    	if (!buffer) return;

    	// If the bits don't match the mask, then bail

    	if (!(logBits & logMask())) return;

    	// Open the file

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (!of.is_open()) return;

    	// Log the output

    	unsigned int	logged = 0;
    	while(logged < count)
    	{
    		// One line at a time...

    		string		line;

    		// The number of characters per line

    		unsigned int	hexLength = 20;

    		// Default the buffer

    		for (unsigned int i = 0; i < hexLength; i++)
    		{
    			line += "-- ";
    		}

    		for (i = 0; i < hexLength; i++)
    		{
    			line += ".";
    		}

    		// Fill it in with real data

    		for (i = 0; i < hexLength && logged < count; i++, logged++)
    		{
    			unsigned char	byte = buffer[logged];
    			unsigned int	index = i * 3;

    			// The hex characters

    			const char	*hexlist="0123456789ABCDEF";
    			line[index+0] = hexlist[byte >> 4];
    			line[index+1] = hexlist[byte & 0xf];

    			// The ascii characters

    			if (byte < 0x20 || byte > 0x7f) byte = '.';
    			line[(hexLength*3)+i+0] = byte;
    		}

    		// Write it to the log file

    		of << headerString(logBits) << line << endl;
    	}
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::indent(const string &s, const LogFlags logBits)
    {
    	// If the bits don't match the mask, then bail

    	if (!(logBits & logMask())) return;

    	// Open the file

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (!of.is_open()) return;

    	// Log the output

    	if (lineCharsFlag())	of << headerString(logBits) << " \xDA " << s << endl;
    	else			of << headerString(logBits) << " +- " << s << endl;

    	// Indent...

    	_indentCount += _indentChars;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	Logger::undent(const string &s, const LogFlags logBits)
    {
    	// If the bits don't match the mask, then bail

    	if (!(logBits & logMask())) return;

    	// Undo the indentation

    	_indentCount -= _indentChars;
    	if (_indentCount < 0) _indentCount = 0;

    	// Open the file

    	ofstream of(logFile().c_str(), ios::out|ios::app);
    	if (!of.is_open()) return;

    	// Log the output

    	if (lineCharsFlag())	of << headerString(logBits) << " \xC0 " << s << endl;
    	else			of << headerString(logBits) << " +- " << s << endl;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    const	string	&Logger::headerString(const LogFlags logBits) const
    {
    	static	string	headerString;
    	headerString.erase();

    	// Get the string that represents the bits

    	switch(logBits)
    	{
    		case LOG_INDENT : headerString += "> "; break;
    		case LOG_UNDENT : headerString += "< "; break;
    		case LOG_ALL    : headerString += "A "; break;
    		case LOG_CRIT   : headerString += "! "; break;
    		case LOG_DATA   : headerString += "D "; break;
    		case LOG_ERR    : headerString += "E "; break;
    		case LOG_FLOW   : headerString += "F "; break;
    		case LOG_INFO   : headerString += "I "; break;
    		case LOG_WARN   : headerString += "W "; break;
    		default:          headerString += "  "; break;
    	}

    	// File string (strip out the path)

    	char	temp[1024];
    	int	ix = sourceFile().rfind('\\');
    	ix = ix == string::npos ? 0: ix+1;
    	sprintf(temp, "%12s[%04d]", sourceFile().substr(ix).c_str(), sourceLine());
    	headerString += temp;

    	// Time string (specially formatted to save room)

    	time_t	t = time(NULL);
    	struct	tm *tme = localtime(&t);
    	sprintf(temp, "%02d/%02d %02d:%02d ", tme->tm_mon + 1, tme->tm_mday, tme->tm_hour, tme->tm_min);
    	headerString += temp;

    	// Spaces for indentation

    	memset(temp, ' ', sizeof(temp));
    	temp[_indentCount] = '\0';

    	// Add the indentation markers

    	int	count = 1;
    	while(count < _indentCount)
    	{
    		if (lineCharsFlag())	temp[count] = '\xB3';
    		else			temp[count] = '|';
    		count += _indentChars;
    	}
    	headerString += temp;

    	return headerString;
    }

    // --------------------------------------------------------------------------------------------------------------------------------
    // Logger - End of file
    // --------------------------------------------------------------------------------------------------------------------------------

Currently browsing [cotd-logger.zip] (6,313 bytes) - [example.cpp] - (3,239 bytes)

    // --------------------------------------------------------------------------------------------------------------------------------
    // example.cpp - example for using the Logger class
    // --------------------------------------------------------------------------------------------------------------------------------

    #include "logger.h"

    // --------------------------------------------------------------------------------------------------------------------------------

    void	generateRandomNumbers()
    {
    	LOGFUNC("generateRandomNumbers()");

    	// Just fill in a buffer of 256 characters with random data

    	char	buffer[256];
    	for (unsigned int i = 0; i < 256; i++)
    	{
    		buffer[i] = rand() % 256;
    	}

    	// Log the output

    	HEX(buffer, sizeof(buffer));
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	logTypes()
    {
    	LOGFUNC("logTypes()");

    	// Log a few different types...

    	LOG("This is an [I]nfo line (notice the 'I' on the far left of the log");
    	string	data("data data data");
    	LOG("Here is some [D]ata: " + data, Logger::LOG_DATA);
    	LOG("A log with the [W]arning flag set", Logger::LOG_WARN);
    	LOG("Here we have an [E]rror entry in the log", Logger::LOG_ERR);
    	LOG("And finally a [!] critical error", Logger::LOG_CRIT);
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	recursion()
    {
    	LOGFUNC("recursion()");

    	static	int	recursionLevel;

    	if (recursionLevel == 3)
    	{
    		LOG("We're inside a recusive routine that has gone three levels deep");
    	}

    	if (recursionLevel < 3)
    	{
    		recursionLevel++;
    		recursion();
    	}
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    void	generateRawData()
    {
    	LOGFUNC("generateRawData()");

    	RAW("\nThis is a test\n  This is only a test\n    This is nothing more than a test of the RAW (unformatted) logging output.\n\n");
    }

    // --------------------------------------------------------------------------------------------------------------------------------

    int	main()
    {
    	LOGFUNC("main()");

    	// Log some various types of entries to the log

    	logTypes();

    	// Log some recursive routines

    	recursion();

    	// Do some indentation. We'll use braces to limit scope of temporary objects that handle the indention for us. When the
    	// braces terminate, so will the scope of the temporary objects, which automatically "pops" the indention stack

    	LOG("Here we have some indentation without having to call multiple routines");

    	{
    		LOGBLOCK("Indention level 1");
    		{
    			LOGBLOCK("Indention level 2");
    			LOG("Hello, from the depths of multiple indentation levels");
    		}
    	}

    	// Let's log some RAW data...

    	generateRawData();

    	// Some random numbers, displayed in hex

    	generateRandomNumbers();

    	// Spit out another line, just for fun

    	logger += "Are we having fun yet? :)";

    	// Done

    	return 0;
    }

    // --------------------------------------------------------------------------------------------------------------------------------

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.