I saw a useful start here:
http://www.cs.technion.ac.il/~imaman/programs/teestream.html
And it works great to make a new stream which goes to both clog and a
I would just use the Boost iostreams stuff to do it.
#include <iostream>
#include <fstream>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>
int main(const int a_argc, const char *a_args[])
{
namespace io = boost::iostreams;
typedef io::tee_device<std::ofstream, std::ostream> TeeDevice;
typedef io::stream<TeeDevice> TeeStream;
std::ofstream flog("logFile.txt");
//We need to copy clog, otherwise we get infinite recursion
//later on when we reassign clog's rdbuf.
std::ostream clogCopy(std::clog.rdbuf());
TeeDevice logTee(flog, clogCopy);
TeeStream logTeeStream(logTee);
logTeeStream << "This text gets clogged and flogged." << std::endl;
//Modify clog to automatically go through the tee.
std::streambuf *originalRdBuf = std::clog.rdbuf(logTeeStream.rdbuf());
std::clog << "This text doesn't only get clogged, it's flogged too." << std::endl;
std::clog.rdbuf(originalRdBuf);
std::clog << "This text avoids flogging." << std::endl;
}
Here is the class I created that seems to do the job, thanks to all who helped out!
-William
class TeeStream : public std::basic_filebuf<char, std::char_traits<char> >
{
private:
class FileStream : public std::ofstream {
public:
FileStream()
: logFileName("/my/log/file/location.log") {
open(logFileName.c_str(), ios::out | ios::trunc);
if (fail()) {
cerr << "Error: failed to open log file: " << logFileName << endl;
exit(1);
}
}
~FileStream() {
close();
}
const char *getLogFileName() const {
return logFileName.c_str();
}
private:
const string logFileName;
};
public:
typedef std::char_traits<char> traits;
typedef std::basic_filebuf<char, traits> baseClass;
TeeStream()
: baseClass(),
_logOutputStream(),
_clogBuf(clog.rdbuf()),
_fileBuf(_logOutputStream.rdbuf()) {
clog.rdbuf(this);
_logOutputStream << "Log file starts here:" << endl;
}
~TeeStream() {
clog.rdbuf(_clogBuf);
}
int_type overflow(char_type additionalChar =traits::eof()) {
const int_type eof = traits::eof();
const char_type additionalCharacter = traits::to_char_type(additionalChar);
const int_type result1 = _clogBuf->sputc(additionalCharacter);
const int_type result2 = _fileBuf->sputc(additionalCharacter);
if (traits::eq_int_type(eof, result1)) {
return eof;
} else {
return result2;
}
}
int sync() {
const int result1 = _clogBuf->pubsync();
const int result2 = _fileBuf->pubsync();
if (result1 == -1) {
return -1;
} else {
return result2;
}
}
private:
FileStream _logOutputStream;
streambuf * const _clogBuf;
streambuf * const _fileBuf;
};
You don't want to do what your've trying to do because the 'tee' is not working at the rdbuf level. So setting the rdbuf to something else will not work, the output will only go to one stream.
You need to follow there example:
e.g.
fstream clog_file(...);
xstream clog_x(...);
TeeStream clog(clog_file, clog_x);
then use clog everywhere instead of your original clog.
If you really want to keep using std::clog for the tee instead of sending output to a different stream, you need to work one level lower: Instead of deriving from ostream, derive from streambuf. Then you can do this:
fstream logFile(...);
TeeBuf tbuf(logFile.rdbuf(), clog.rdbuf());
clog.rdbuf(&tbuf);
For more information on how to derive your own streambuf class, see here.