i would like to create a flexible logger class. I want it to be able to output data to a file or to standard output. Also, i want to use streams. The class should look something like:
class Logger
{
private:
std::ostream m_out; // or ofstream, iostream? i don't know
public:
void useFile( std::string fname);
void useStdOut();
void log( symbol_id si, int val );
void log( symbol_id si, std::string str );
//etc..
};
The symbol_id
is an enum and defines the formatting. What i want to achieve is to be able to easily switch from standart output to a file and vice versa (this is the purpose of the use*
methods). Preferably by just using m_out
and simply writing m_out << "something";
without any checks whether i want to write to a file or stdout.
I know there are many ways how to get around this (using if's
to test if i want to write to a file or stdout, the "C way" (using FILE*
and fprintf
)) and so on, but i'm sure there must be a way how to achieve this with C++ streams in a nice way. But i can't seem to find the way how to do it. Can somebody help me please?
Dr. Dobbs published an article that I've used as inspiration for logging. It's worth a read. http://www.drdobbs.com/cpp/201804215
It looks like another article has been published more recently too, but I have not read it. http://www.drdobbs.com/cpp/225700666
The
std::o*stream
classes in C++ inherit from std::ostream. This means you should write your interface depending on a std::ofstream pointer or reference:The way I've attacked this problem before is to make
Logger
an abstract base class and create separateFileLogger
andOutStreamLogger
classes. Then create aCompositeLogger
object that implements theLogger
interface, which just outputs all loggers:If you don't need this level of flexibility and want to keep all this in a single class you could make the m_Out variable a pointer to
std::ostream
and add an extra flag to keep track of whether you need to delete it on cleanup:Obviously you'd need some more checks in
useFile
anduseStdOut
to prevent memory leaks.A variation to the_mandrill solution, for that I thought a Strategy pattern would fit this problem better, conceptually.
We can change the log strategy at any time just by calling context->SetLogger.
We can also use different files for the file logger.