One solution is to duplicate everything that is written to cerr into for example a file.
This is the helper class:
class CTee {
public:
// Use ostream &s2 if you want to duplicate to an ostream, pass other
// stuff you need if you have other logging mechanisms.
CTee(ostream &s1, ostream &s2) : m_s1(s1), m_s1OrigBuf(s1.rdbuf()), m_teebuf(s1.rdbuf(), s2.rdbuf()) { s1.rdbuf(&m_teebuf); }
~CTee() { m_s1.rdbuf(m_s1OrigBuf); }
private:
CTee &operator =(CTee &rhs); // not implemented
class CTeeBuf : public streambuf {
public:
// Use streambuf *sb2 if you want to duplicate to an ostream/streambuf.
// Pass other Information if you want to log to something different.
CTeeBuf(streambuf* sb1, streambuf* sb2) : m_sb1(sb1), m_sb2(sb2) {}
protected:
virtual int_type overflow(int_type c) {
if(streambuf::traits_type::eq_int_type(c, streambuf::traits_type::eof()))
return c;
else {
// Put char to cerr/stream to duplicate
m_sb1->sputc((streambuf::char_type)c);
// Put char to duplicate stream. If you want to duplicate to something
// different, then write the char whereever you want to.
return m_sb2->sputc((streambuf::char_type)c);
}
}
virtual int sync() {
m_sb1->pubsync();
return m_sb2->pubsync();
}
// Store streambuf *m_sb2 if you want to duplicate to streambuf.
// Store anything else if you want to duplicate to something different.
streambuf *m_sb1, *m_sb2;
};
ostream &m_s1;
streambuf * const m_s1OrigBuf;
CTeeBuf m_teebuf;
};
CTee takes an ostream
to duplicate and an ostream
to duplicate to. It takes the ostream that shall be duplicated and replaces it's rdbuf
, the streambuf
that is written to, with a CTeeBuf (see CTee ctor). CTeeBuf takes the char
s that are written to it and forwards them to the streambuf
s of both ostream
s (see CTeeBuf::overflow and CTeeBuf::sync). The CTee dtor reverts the changed streambuf
s to their original values.
And it is used like this:
char logfilename[] = "myfile.log";
ofstream logfile(logfilename, ios_base::out | ios_base::app);
CTee tee(cerr, logfile);
From now on everything written to cerr will be duplicated to logfile (during the lifetime of tee). So this message will be written to cerr, but also to logfile:
cerr << "error occured: ..." << endl;
It is also possible to write to other ostreams than a logfile. If you don't want to duplicate to another ostream
but to something else, just replace CTeeBuf::overflow by an implementation that logs whereever you want to.
See also http://www.cs.technion.ac.il/~imaman/programs/teestream.html and http://wordaligned.org/articles/cpp-streambufs.