Several approaches to a logging class I'm making a personal logger and so far I have come up with 3 solutions.
I have them simplified in this example to highlight my dilemma. I want to know the pros and cons of my approaches and ways to make them better. Also should I be flushing the stream as often as possible and why?

TempLog

This version will be created and destroyed on every log. Endline forced on destruction.

class TempLog
{
public:
    std::ostream&amp; log()
    {
        return std::cout;
    }
    ~TempLog()
    {
        std::cout &lt;&lt; "\n";
    }
};


SingletonLog_ver_1

If the endline is forced in this manner using the same ostream from outside the logger will result in bad output. Also it feels somewhat dishonest but due to my lack of experience I don't trust my feeling.

class SingletonLog_ver_1
{
public:
    static SingletonLog_ver_1 instance()
    {
        static SingletonLog_ver_1 instance;
        return instance;
    }

    std::ostream&amp; log()
    {
        static bool firstLine = true;
        if (firstLine)
            firstLine = false;
        else
            std::cout &lt;&lt; "\n";

        return std::cout;
    }

private:
    SingletonLog_ver_1() {};
};


SingletonLog_ver_2

Now while I like what is happening here more than the previous two examples it seems like the least elegant code. Is there another more elegant way to achieve this kind of functionality?

class SingletonLog_ver_2
{
public:
    static SingletonLog_ver_2 instance()
    {
        static SingletonLog_ver_2 instance;
        return instance;
    }

    std::pair&lt;std::ostream&amp;, std::unique_ptr&lt;TempObj_NewLine&gt;&gt; log()
    {   
        return std::pair&lt;std::ostream&amp;, std::unique_ptr&lt;TempObj_NewLine&gt;&gt;(std::cout, std::make_unique&lt;TempObj_NewLine&gt;());
    }

private:
    SingletonLog_ver_2() {};
};


TempObj_NewLine

class TempObj_NewLine
{
public:
    TempObj_NewLine() {};
    ~TempObj_NewLine()
    {
        std::cout &lt;&lt; "\n";
    }
};


Use of the 3 loggers:

int main()
{
    TempLog().log() &lt;&lt; "first log!" &lt;&lt; " first log, same line.";
    TempLog().log() &lt;&lt; "second log." &lt;&lt; "second line.";

    SingletonLog_ver_2::instance().log().first &lt;&lt; "first line singleton log!"&lt;&lt; " first line, again.";
    SingletonLog_ver_2::instance().log().first &lt;&lt; "second line singleton log!";

    SingletonLog_ver_1::instance().log() &lt;&lt; "first line singleton - hacky endline." &lt;&lt; " first line, again.";
    SingletonLog_ver_1::instance().log() &lt;&lt; "second line singleton - hacky endline";

    std::cin.get();
}
Singletons

There are several ways to make something a singleton. You are doing it by removing the public constructor, and forcing users to get an instance through a static member function. That works, but there are alternatives. You can create a regular class, and provide a global instance of that class. Just like so:

class Log {
    ...
};

Log logInstance;


You then just refer to logInstance instead of SingletonLog::instance().

Proper naming

Your class has a function log(), however that function does not log anything. Instead, it just returns a std::ostream reference. A more appropriate name for the function would then be get_stream().

If you intend to always use it as SingletonLog::instance().log() &lt;&lt; "some string", then it might be better to just add an operator &lt;&lt;() overload to your logging class. This has several advantages:


It's less typing: SingletonLog::instance() &lt;&lt; "some string", or when using the alternative way of making a singleton: logInstance &lt;&lt; "some string".
You can actually read the contents of the string in your class, and check whether it ends with a newline or not.


Flushing

Whether or not to flush depends on what you want to log. If you log a lot of information, flushing adds considerable overhead. However, if you want to be sure that any errors that are logged are not lost when your program terminates abnormally, then it might be prudent to flush the output stream after every write to the log.

Other suggestions

Do you really want to use the &lt;&lt; operator? If you rather like to use the printf() style, then consider making use of fmtlib, which provides a type-safe way to format strings the printf() or Python .format() way. Your logging class could look like:

#include &lt;fmt/format.h&gt;

class Log {
    template &lt;typename... Args&gt;
    void log(const std::string &amp;format, const Args &amp;... args) {
        std::cout &lt;&lt; fmt::format(format, args...);
    }
};

Log logInstance;


And use it like:

logInstance.log("First line: {}\nSecond line: {}\n", "first entry", "second entry");