C++ simple logging class with UTF-8 output [code example]
- by Andrew
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.