How to redefine clog to tee to original clog and a

2019-01-24 17:07发布

问题:

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 log file.

However, if I try to redefine clog to be the new stream it does not work because the new stream has the same rdbuf() as clog so the following has no effect:

clog.rdbuf(myTee.rdbuf());

So how can I modify the tee class to have its own rdbuf() which can then be the target of clog?

Thanks.

-William

回答1:

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.



回答2:

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.



回答3:

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;

};


回答4:

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;
}