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()
?
-
1\$\begingroup\$ Is this what you are looking for? stackoverflow.com/a/1761027/14065 \$\endgroup\$Loki Astari– Loki Astari2016年12月20日 22:17:47 +00:00Commented Dec 20, 2016 at 22:17
1 Answer 1
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.
-
\$\begingroup\$
auto log = logstream("logfile.txt");
doesn't build even though I added parameter to constructor to take filename. \$\endgroup\$zadane– zadane2016年12月22日 15:29:28 +00:00Commented Dec 22, 2016 at 15:29 -
\$\begingroup\$ You need move constructor for it to work. I was just illustrating an example \$\endgroup\$Emily L.– Emily L.2016年12月22日 19:54:36 +00:00Commented Dec 22, 2016 at 19:54
-
\$\begingroup\$ Can you post it here? \$\endgroup\$zadane– zadane2016年12月22日 21:34:48 +00:00Commented Dec 22, 2016 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.– Emily L.2016年12月23日 18:02:39 +00:00Commented Dec 23, 2016 at 18:02