4
\$\begingroup\$

I want to create a generic log file which I can easily reuse in multiple files. Also, I want to be able to use the versatile std::cout so I can easily write formatted text (as oppose to create my own write function which may write only strings and which can probably never be that versatile).

Definition

#include <fstream>

class logstream :
    public std::ofstream
{
public:
    logstream();
    ~logstream();

protected:
    std::streambuf *psbuf, *backup;
    std::ofstream filestr;
};

Implementation

logstream::logstream()
{
    filestr.open("test_streaming.txt", std::ofstream::app);

    backup = std::clog.rdbuf();     // back up cout's streambuf

    psbuf = filestr.rdbuf();        // get file's streambuf
    std::clog.rdbuf(psbuf);         // assign streambuf to cout
}


logstream::~logstream()
{
    std::clog.rdbuf(backup);        // restore cout's original streambuf

    filestr.close();
}

Usage:

void CExportDlg::OnBnClickedButtonLog()
{
    logstream log; // redirects clog and restores it when it goes out of scope

    std::clog << "hello this goes into log file" << std::endl;
    std::clog << "And I can readily write intetgers like " << 5 << "this one" << std::endl;
}

In reality, I have put the line logstream log in the main application class so now I can use only std:clog() anywhere in the application and output goes to a log file. But can this be improved?

What if I want to create multiple log files for different purposes? Is there a way I can access clog() (or cout()) by the object so it goes into that object only, like log.clog()?

\$\endgroup\$
3
\$\begingroup\$

I like how you use the existing stream API instead of rolling your own. Also I think that redirecting std::clog is a good approach as this could then easily be retro fitted into an existing application that properly uses std::clog with ease.

However there are some problems.

Usage

The inheritance of from std::ofstream implies a usage such as this:

logstream log("logfile.txt");
log<<"foobar\n";

but this is not the intended usage of the class. I also see no reason why this class must inherit from std::ofstream. I would remove this inheritance because in my opinion it breaks the Liskov Substitution Principle.

Scoped design

While a scoped design is good in some/many cases I don't think this is one of them. Typically logging destinations is not something you configure per scope but rather once for the application. This is merely a personal opinion I'm sure it has its uses.

Usability

I'm missing some features. One particular that comes to mind is to write to file and console at the same time (as the example by @LokiAstari here).

Consider using an already existing library

Like for example log4cpp which provides log streams which you can redirect std::clog to and use separately. It is configurable and has all the bells and whistles you usually want from a log system.

\$\endgroup\$
  • \$\begingroup\$ auto log = logstream("logfile.txt"); doesn't build even though I added parameter to constructor to take filename. \$\endgroup\$ – zadane Dec 22 '16 at 15:29
  • \$\begingroup\$ You need move constructor for it to work. I was just illustrating an example \$\endgroup\$ – Emily L. Dec 22 '16 at 19:54
  • \$\begingroup\$ Can you post it here? \$\endgroup\$ – zadane Dec 22 '16 at 21:34
  • \$\begingroup\$ @zadane This is "code review" not "write code for me". If you need help writing a move constructor ask in stackoverflow. \$\endgroup\$ – Emily L. Dec 23 '16 at 18:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.