Hagadol
Hagadol

Reputation: 362

Logging stdin and stdout

Is there a (ideally simple and elegant) way to log stdin and stdout?

Note, that I do not intend to redirect the streams. I want the standard streams to remain functional to communicate with other software while also writing all inter-process communication to some file.

Upvotes: 1

Views: 1501

Answers (2)

Michael Veksler
Michael Veksler

Reputation: 8475

Option 1:

As @PaulR suggests, you can use an external process such as tee (on Linux/Mac/Unix), or write your own process to read from stdin in a loop and write to stdout and another file.

Option 2:

I have done this many years ago with std::basic_ios::rdbuf for std::cout. All one has to do is to define a class (see std::filebuf, and std::streambuf):

class tee_buf : public std::filebuf {
   public:
     // Not an owing pointer
     tee_buf(std::streambuf * other_stream) 
        : m_other_stream(other_stream) {}
     void swap( tee_buf& rhs );
     // No need to override open/close since we need to manage only the file.
     // The open/close calls don't touch the other_stream.
   protected:
     int_type overflow(int_type c = traits_type::eof()) override;

     // The parent calls this->overflow(), but then still need to call
     // m_other_stream->sync(). This is problematic since m_other_stream
     // gets flushed twice.
     int sync() override;

     pos_type seekoff( off_type off,
                      std::ios_base::seekdir dir,
                      std::ios_base::openmode which) override {
        return pos_type(off_type(-1)); // ???
     }
     pos_type seekpos( pos_type sp,
                      std::ios_base::openmode which) override {
        return pos_type(off_type(-1)); // ???
     }
     ....

This is more efficient for intensive IO as it avoids the middle-man. But in most cases the tee solution is simpler and preferable. If performance is an issue (which in most cases it isn't), then it may be possible to make both stream buffers share a single memory buffer. It may also be possible to use async IO to write to both streams in parallel.

Usage with memory leak:

std::cout.rdbuf(new tee_buf(std::cout.rdbuf());

Usage without memory leak:

Write a RAII class to contain the tee_buf, to save the original and set the new std::cout.rdbuf(). Upon destruction restore the state of std::cout.rdbuf(). Make a single instance of this class, which will do the dirty work upon its construction and destruction.

As for the C style stdout: I don't believe that there is a way to override its behavior. At most it is possible to play with buffer memory, but that is not enough to get the desired functionality. With stdout the only thing one can do is use a tee-like solution.

Upvotes: 1

Paul R
Paul R

Reputation: 213100

It should be fairly simple to this do with tee. This would allow you to maintain any existing redirection while also sending stdin/stdout elsewhere (to files in your case). This also avoids having to make any modifications to your existing code.

Upvotes: 0

Related Questions