A generic logger in modern C++ I've created a simple logging system that I'm planning to use and I've tried out some modern C++ features to make it generic. First I've made a log class that represents a single log message:

struct log : std::stringstream
{
private:
    template &lt;typename Level, size_t I = 0, typename... Stamps&gt;
    static inline typename std::enable_if&lt;I == sizeof...(Stamps), void&gt;::type
        __stamp(log&amp; l)
    {
    }

    template &lt;typename Level, size_t I = 0, typename... Stamps&gt;
    static inline typename std::enable_if&lt;I &lt; sizeof...(Stamps), void&gt;::type
        __stamp(log&amp; l)
    {
        l &lt;&lt; std::tuple_element_t&lt;I, std::tuple&lt;Stamps...&gt;&gt;::Get&lt;Level&gt;();
        __stamp&lt;Level, I + 1, Stamps...&gt;(l);
    }

public:
    template &lt;typename Level, typename... Stamps&gt;
    static log&amp; emit()
    {
        static log l;
        __stamp&lt;Level, 0, Stamps...&gt;(l);
        l &lt;&lt; ": ";
        return l;
    }

    template &lt;typename T&gt;
    log&amp; operator&lt;&lt;(T el)
    {
        *((std::stringstream*)this) &lt;&lt; el;
        return *this;
    }

    template &lt;typename Logger&gt;
    void Flush()
    {
        Logger::Log(str().c_str());
        clear();
    }
};

template &lt;typename Logger&gt;
std::ostream&amp; endlog(std::ostream&amp; out)
{
    ((log*)&amp;out)-&gt;Flush&lt;Logger&gt;();
    return out;
}

typedef Logger&lt;ConsoleSink&gt; DefaultLogger;
std::ostream&amp; endlog(std::ostream&amp; out)
{
    ((log*)&amp;out)-&gt;Flush&lt;DefaultLogger&gt;();
    return out;
}


And I've created the main logger:

template &lt;typename... Sinks&gt;
class Logger
{
private:
    static std::tuple&lt;Sinks...&gt; s_Sinks;

private:
    template &lt;size_t I = 0, typename... Tp&gt;
    static inline typename std::enable_if&lt;I == sizeof...(Tp), void&gt;::type
        __Log(const char* msg)
    {
    }

    template &lt;size_t I = 0, typename... Tp&gt;
    static inline typename std::enable_if&lt;I &lt; sizeof...(Tp), void&gt;::type
        __Log(const char* msg)
    {
        std::get&lt;I&gt;(s_Sinks).Write(msg);
        __Log&lt;I + 1, Tp...&gt;(msg);
    }

public:
    static void Log(const char* msg)
    {
        __Log&lt;0, Sinks...&gt;(msg);
    }
};

template &lt;typename... Sinks&gt;
std::tuple&lt;Sinks...&gt; Logger&lt;Sinks...&gt;::s_Sinks;


An example usage would be:

log::emit&lt;Info, LevelStamp, SeparatorStamp, TimeStamp&gt;() &lt;&lt; "Hello, Logging " &lt;&lt; 3 &lt;&lt; endlog;


Now I can see a lot of problems with my own code, for example because of my emit function is defined with variadic stamps, there's no way to define a default overload for it so if I always want the same stamps, I have to type them out. Another is that my DefaultLogger is not configurable from outside the logger code which I think should be a feature.

Is my approach any good? How could I improve it?
Stop using double underscore.
The double underscore is reserved for the implementation it definitely should not be used in "user code".

See: What are the rules about using an underscore in a C++ identifier?

Recursive template Call.

You use a recursive technique to call the Write() method for all your sinks. I prefer to use the loop technique (I would say this is a more modern approach but I have no reference for that). But I find it clearer as you don't need the empty terminating function call.

private:
    using Tuple=std::tuple&lt;Sinks...&gt;;

    template&lt;std::size_t... index&gt;
    static void writeToLogs(char const* m, std::integer_sequence&lt;std::size_t, index...&gt; const&amp;)
    {
        auto ignore = {(std::get&lt;index&gt;(s_Sinks).Write(m),true)...};
    }

public:
    static void Log(const char* msg)
    {
        writeToLogs(msg, std::make_index_sequence&lt;std::tuple_size&lt;Tuple&gt;::value&gt;());
    }


Design

Everybody and their grandson tends to want to write a logger at some point in time. The trouble is we already have a good logger built into the *nix world (syslog). If you are not using syslog to do your logging you are doing it wrong.

It (syslog) already does everything you need to do and more and there is a whole ecosystem built around it for monitoring. Now there is nothing wrong with writing a wrapper around syslog but you have to be smart about it (the current ones are very smart).

Log(debug, getSomeMessage());


This should not call the function getMessage() if the debugging level is set to production. That would be a complete wast of time and resources as debug messages are currently being ignored.