Hello everyone,
I was working on one of my academic projects and for the first time I needed pure C++ without GUI.
After googling for a while, I did not find any simple and easy to use implementation for logging and created my own.
This is a simple implementation with iostreams that logs messages to screen and to the file simultaneously. I was thinking of using templates but then I realized that I do not expect any changes and removed that.
It is modified std::wostream with two added modifiers:
1. TimeStamp - prints time-stamp
2. LogMode(LogModes) - switches output: file only, screen only, file+screen.
*Boost::utf8_codecvt_facet* is used for UTF-8 output.
// ############################################################################
// # Name:              MyLog.h                                               #
// # Purpose:           Logging Class Header                                  #
// # Author:            Andrew Drach                                          #
// # Modified by:       <somebody>                                            #
// # Created:           03/21/10                                              #
// # SVN-ID:            $Id$                                                  #
// # Copyright:         (c) 2010 Andrew Drach                                 #
// # Licence:           <license>                                             #
// ############################################################################
#ifndef INCLUDED_MYLOG_H
#define INCLUDED_MYLOG_H
// headers --------------------------------------------------------------------
#include <string>
#include <iostream>
#include <fstream>
#include <exception>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
using namespace std;
// definitions ----------------------------------------------------------------
// ----------------------------------------------------------------------------
// DblBuf class
// Splits up output stream into two
// Inspired by http://wordaligned.org/articles/cpp-streambufs
// ----------------------------------------------------------------------------
class DblBuf : public   wstreambuf
{
private:    // private member declarations
        DblBuf();
        wstreambuf *bf1;
        wstreambuf *bf2;
        virtual int_type overflow(int_type ch)
            {
                int_type eof = traits_type::eof();
                int_type not_eof = !eof;
                if ( traits_type::eq_int_type(ch,eof) )
                    return not_eof;
                else {
                    char_type ch1 = traits_type::to_char_type(ch);
                    int_type r1( bf1on ? bf1->sputc(ch1) : not_eof );
                    int_type r2( bf2on ? bf2->sputc(ch1) : not_eof );
                    return (traits_type::eq_int_type(r1,eof) ||
                                    traits_type::eq_int_type(r2,eof) ) ? eof : ch;
                    }
            }
        virtual int sync()
            {
                int r1( bf1on ? bf1->pubsync() : NULL );
                int r2( bf2on ? bf2->pubsync() : NULL );
                return (r1 == 0 && r2 == 0) ? 0 : -1;
            }   
public:     // public member declarations
        explicit DblBuf(wstreambuf *bf1, wstreambuf *bf2) : bf1(bf1), bf2(bf2)
            {
                if (bf1) bf1on = true;
                else     bf1on = false;
                if (bf2) bf2on = true;
                else     bf2on = false;
            }
        bool bf1on;
        bool bf2on;
};
// ----------------------------------------------------------------------------
// logstream class
// Wrapper for a standard wostream with access to modified buffer
// ----------------------------------------------------------------------------
class logstream : public wostream
{
private:    // private member declarations
        logstream();
public:     // public member declarations
        DblBuf *buf;
        explicit logstream(wstreambuf *StrBuf, bool isStd = false) : 
                                wostream(StrBuf, isStd), buf((DblBuf*)StrBuf) {}
};
// ----------------------------------------------------------------------------
// Logging mode Class
// ----------------------------------------------------------------------------
enum LogModes{LogToFile=1, LogToScreen, LogToBoth};
class LogMode
{
private:    // private member declarations
    LogMode();
        short mode;
public:     // public member declarations
        LogMode(short mode1) : mode(mode1) {}
    logstream& operator()(logstream &stream1)
            {
                switch(mode) {
                    case LogToFile:
                        stream1.buf->bf1on = true;
                        stream1.buf->bf2on = false;
                    break;
                    case LogToScreen:
                        stream1.buf->bf1on = false;
                        stream1.buf->bf2on = true;
                    break;
                    case LogToBoth:
                        stream1.buf->bf1on = true;
                        stream1.buf->bf2on = true;
                    }
                return stream1;
            }
};
logstream& operator<<(logstream &out, LogMode mode) { return mode(out); }
wostream& TimeStamp1(wostream &out1)
{
time_t time1;
struct tm timeinfo;
wchar_t timestr[512];
// Get current time and convert it to a string
time(&time1);
localtime_s (&timeinfo, &time1);
wcsftime(timestr, 512,L"[%Y-%b-%d %H:%M:%S %p] ",&timeinfo);
return out1 << timestr;
}
// ----------------------------------------------------------------------------
// MyLog class
// Logs events to both file and screen
// ----------------------------------------------------------------------------
class MyLog
{
private:    // private member declarations
        MyLog();
        auto_ptr<DblBuf> buf;
        string mErrorMsg1;
        string mErrorMsg2;
        string mErrorMsg3;
        string mErrorMsg4;
public:     // public member declarations
        explicit MyLog(string FileName1, wostream *ScrLog1, locale utf8locale1);
        ~MyLog();
        void NewEvent(wstring str1, bool TimeStamp = true);
        string FileName;
        wostream *ScrLog;
        wofstream File;
        auto_ptr<logstream> Log;
        locale utf8locale;
};
// ----------------------------------------------------------------------------
// MyLog constructor
// ----------------------------------------------------------------------------
MyLog::MyLog(string FileName1, wostream *ScrLog1, locale utf8locale1) : 
                // ctors
                mErrorMsg1("Failed to open file for application logging! []"),
                mErrorMsg2("Failed to write BOM! []"),
                mErrorMsg3("Failed to write to file! []"),
                mErrorMsg4("Failed to close file! []"),
                FileName(FileName1),
                ScrLog(ScrLog1),
                utf8locale(utf8locale1),
                File(FileName1.c_str())
{
// Adjust error strings
mErrorMsg1.insert(mErrorMsg1.length()-1,FileName1);
mErrorMsg2.insert(mErrorMsg2.length()-1,FileName1);
mErrorMsg3.insert(mErrorMsg3.length()-1,FileName1);
mErrorMsg4.insert(mErrorMsg4.length()-1,FileName1);
// check for file open errors
if ( !File ) throw ofstream::failure(mErrorMsg1);
// write UTF-8 BOM
File << wchar_t(0xEF) << wchar_t(0xBB) << wchar_t(0xBF);
// switch locale to UTF-8
File.imbue(utf8locale);
// check for write errors
if ( File.bad() ) throw ofstream::failure(mErrorMsg2);
buf.reset( new DblBuf(File.rdbuf(),ScrLog->rdbuf()) );
Log.reset( new logstream(&*buf) );
}
// ----------------------------------------------------------------------------
// MyLog destructor
// ----------------------------------------------------------------------------
MyLog::~MyLog()
{
*Log << TimeStamp1 << "Log finished." << endl;
// clean up objects
Log.reset();
buf.reset();
File.close();
// check for file close errors
if ( File.bad() ) throw ofstream::failure(mErrorMsg4);
}
//---------------------------------------------------------------------------
#endif // INCLUDED_MYLOG_H
Tested on MSVC 2008, boost 1.42.
I do not know if this is the right place to share it. Hope it helps anybody. Feel free to make it better.