如何将clog重新定义为同时输出到原始clog和日志文件的tee方式?

4
我在这里看到了一个有用的开始:

http://www.cs.technion.ac.il/~imaman/programs/teestream.html

它可以很好地创建一个新流,该流同时输出到clog和日志文件中。
然而,如果我尝试重新定义clog为新流,则无法成功,因为新流具有与clog相同的rdbuf(),所以以下操作没有效果:
clog.rdbuf(myTee.rdbuf());

我应该如何修改tee类,使其拥有自己的rdbuf(),从而成为clog的目标?
谢谢。
-威廉
4个回答

3

如果你真的想继续使用std::clog作为tee,而不是将输出发送到其他流中,那么你需要在更低的一级上操作:不要从ostream派生,而是从streambuf派生。然后你可以这样做:

fstream logFile(...);
TeeBuf tbuf(logFile.rdbuf(), clog.rdbuf());
clog.rdbuf(&tbuf);

如需了解如何派生自己的streambuf类,请参见此处


你好,谢谢你的建议。考虑到这一点,我在想对于我的TeeBuf类,我是否应该重载虚拟xsputn()函数来向两个流中写入输出?这样就足够了吗?换句话说,我正在寻找与原始解决方案中operator<<()相当的等效方案,只是更高一个级别而已。谢谢。
  • William
- WilliamKF
好的,我已经编写了一个版本,覆盖了xsputn()并发送到两个底层streambuf。然而,只有第一行有效,并且被发送到clog和日志文件,一旦看到'endl',就不再输出到clog或文件。那么,我需要在新的tee缓冲区中正确处理'endl'以使clog正常工作? - WilliamKF
这里是提议的xsputn():流大小xsputn(const char_type *charSequence, streamsize numChar) { _clogBuf->sputn(charSequence, numChar); 返回_fileBuf->sputn(charSequence, numChar); } - WilliamKF
我尝试添加seekoff()、seekpos()和sync()的定义,它们只是使用底层的两个缓冲区调用pubseekoff()、pubseekpos()和pubsync(),但我得到了相同的症状,只有第一行到'endl'起作用。 - WilliamKF
你不需要xsputn,那只是为了优化,你需要重载sync()和overflow()。每当内部缓冲区应该与底层表示(例如屏幕)同步时(例如在每个std::flush、std::endl上),都会调用sync(),因此请确保它能够实现这一点。当内部缓冲区填满时,将调用overflow(),因此它应该同步以释放缓冲区,并检查eof()等。 - Pieter

2

你不想做你一直尝试做的事情,因为'tee'在rdbuf级别上不起作用。所以将rdbuf设置为其他内容是不起作用的,输出只会流向一个流。

你需要按照以下示例进行操作:

例如:

fstream clog_file(...);
xstream clog_x(...);
TeeStream clog(clog_file, clog_x);

然后在所有地方都使用clog,而不是您原来的clog。

大家好,有没有人有一个重新定义C++内置clog的例子,以便拥有一个新的相关rdbuf(),该rdbuf()被处理为原始clog.rdbuf()和写入磁盘上日志文件的ofstream对象的rdbuf()。目的是让代码在整个过程中使用std::clog,但同时将其发送到默认的clog目标以及磁盘上的日志文件。谢谢。-William - WilliamKF
看看Nathan的回答。虽然更费力,但它会起作用。如果你想走这条路,我建议你买一本《C++标准库:教程与参考》。我仍然建议使用自己的typedef,这样你就不会被clog所束缚。http://www.amazon.com/C-Standard-Library-Tutorial-Reference/dp/0201379260/ref=pd_sim_b_1 - Shane Powell

1
这是我创建的类,似乎可以完成任务,感谢所有帮助过我的人!-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;

};

虽然总体想法看起来没问题,但你可能会遇到一些潜在的问题。putc 函数期望传入 char_type 类型,而你却传入了 int_type 类型,这时可以使用 traits::to_char_type() 函数进行转换(并适当检查是否转换成功)。 - Pieter
其次,最好使用特质比较函数来比较内部int_type,而不是依赖于operator==traits::eq_int_type(additionalChar, traits::eof())(虽然这在实际情况下并没有太大影响) - Pieter
根据Pieter的建议进行了修改。 - WilliamKF

1
我会使用Boost iostreams库来完成它。 #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;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接