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 chars that are written to it and forwards them to the streambufs of both ostreams (see CTeeBuf::overflow and CTeeBuf::sync). The CTee dtor reverts the changed streambufs 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.