Radnoraj,
I also needed this kind of functionality, so I wrote a framework which
allows you to register any number of streams, then use a `dispatcher'
stream which wil write any output to all of the streams which you
registered.
The basic idea is as follows:
1. Have a singleton registry object (LogRegistry), which holds pointers
to the output streams
2. Implement the dispatching mechanism by writing a LogBuffer class,
derived from std::streambuf. The flush() function in this buffer gets
all the registered streams from the LogRegistry object, and writes to
each one in turn. (See Josuttis if you're not up on the internals of
iostreams)
3. The class which you actually use on the lhs of your << operators,
LogStream, is simply a std:

stream whose internal buffer is an
instance of LogBuffer.
I added one extra feature which is that each registered stream is
associated with a `logging level'. This is just a number which allows
you to choose which stream(s) each message is sent to. In this way you
can have a main log file to which all messages get sent, an error file
which only receives warning messages, and an error file to which any
serious errors are written. The user selects the appropriate level for
any given message using stream manipulators.
The following code, which should compile standalone, illustrates the
idea - you should be able to hack this to suit your needs.
HTH
Gareth
//----------------
// Interface
//-------------
#include <streambuf>
#include <ostream>
#include <iomanip>
#include <map>
// A class with which users can register logging streams
struct LogRegister {
typedef std::multimap<unsigned, std:

stream*> log_map_t;
log_map_t log_map;
void register_stream(std:

stream& stream, unsigned pri);
void write(const std::string& s, unsigned level) const;
static LogRegister& instance();
private:
LogRegister();
};
// Forward declaration
class LogStream;
// Custom streambuf
struct LogBuffer : public std::streambuf {
LogBuffer(LogStream* s);
~LogBuffer();
private:
// The buffer holds a pointer to the stream, as it needs to query
// the stream to obtain the current logging level
LogStream* stream_ptr;
std::string buffer;
int_type overflow(int_type i);
void flush();
};
// The actual stream class (the `message dispatcher')
class LogStream : public std:

stream {
LogBuffer buffer;
public:
// Index into the ios state array
static const int level_index;
LogStream();
unsigned get_level();
};
// Manipulators allowing the user to specify logging levels
struct LogLevelManipulator {
unsigned value;
LogLevelManipulator(unsigned v) : value(v) { }
};
// This is just a convenience function for creating level manipulators
LogLevelManipulator log_level(unsigned n);
// The function which modifies the stream's logging level according to
// a manipulator object
std:

stream& operator<<(std:

stream& out, LogLevelManipulator l);
//--------------------
// Implementation
//------------------
void LogRegister::register_stream(std:

stream& stream, unsigned pri)
{ log_map.insert(std::make_pair(pri, &stream)); }
void LogRegister::write(const std::string& s, unsigned level) const {
for(log_map_t::const_iterator i = log_map.lower_bound(level);
i != log_map.end(); ++i)
*(i->second) << s;
}
LogRegister& LogRegister::instance()
{ static LogRegister lr; return lr; }
LogRegister::LogRegister()
{ }
LogBuffer::LogBuffer(LogStream* s)
: stream_ptr(s)
{ }
LogBuffer::~LogBuffer()
{ flush(); }
LogBuffer::int_type LogBuffer:

verflow(int_type i) {
if(!traits_type::eq_int_type(i, traits_type::eof())) {
char_type c = traits_type::to_char_type(i);
buffer.push_back(c);
if(c == '\n') flush();
}
return traits_type::not_eof(i);
}
void LogBuffer::flush() {
LogRegister::instance().write(buffer, stream_ptr->get_level());
buffer.clear();
}
// Stream class
LogStream::LogStream()
: std:

stream(&buffer), buffer(this)
{ }
unsigned LogStream::get_level()
{ return iword(level_index); }
const int LogStream::level_index = std::ios::xalloc();
// Manipulators
LogLevelManipulator log_level(unsigned n)
{ return LogLevelManipulator(n); }
std:

stream& operator<<(std:

stream& out, LogLevelManipulator l) {
// First flush the stream
out << std::flush;
// Now set the level
out.iword(LogStream::level_index) = l.value;
return out;
}
//----------------------
// Example
//--------------
#include <fstream>
#include <iostream>
#include <stdexcept>
int main() {
std:

fstream log_stream("/tmp/test.log");
std:

fstream error_stream("/tmp/test.err");
// Register the streams. The higher the level, the more messages
// that stream will receive. Here std::cout is reserved for only
// the serious messages which the user needs to see right now.
LogRegister::instance().register_stream(std::cout, 0);
LogRegister::instance().register_stream(error_stream, 1);
LogRegister::instance().register_stream(log_stream, 2);
// This is the `dispatcher' object
LogStream multi;
// Define some level manipulators
const LogLevelManipulator
normal = log_level(2),
error = log_level(1),
fatal = log_level(0);
try {
// Set the initial logging level
multi << normal;
// Most program output just goes to the log; not printed to the
// console
multi << "Standard message; just sent to the 'log' stream"
<< std::endl;
multi << "Another routine message" << std::endl;
// Oops, an error occurred - this is logged to error_stream
// but does not abort the program
multi << error << "A nasty error occurred..." << std::endl
<< normal;
// Note that the log level has been reset to `normal'
// ... and back to routine logging
multi << "Back to normal" << std::endl;
// Now something nasty happens, resulting in an uncaught
// exception propagating to the top level. This will abort the
// program.
throw std::runtime_error("Blah blah");
}
catch(std::exception& e) {
multi << fatal << "A catastrophic error occurred:" << std::endl
<< e.what() << std::endl
<< "Aborting..." << std::endl;
log_stream.close();
error_stream.close();
exit(1);
}
}