#include <iostream>

// stub logging object to make the example work - just logs to stdout
struct Log
{
    template<typename T>
    friend Log& operator<<(Log& l, T d)
    {
        std::cout << d;
        return l;
    }
    friend Log& operator<<(Log& l, std::ostream& (*f)(std::ostream&))
    {
        std::cout << f;
        return l;
    }
};
Log gLog;

#define LOG Base::GetLog()

// GetLog in the global namespace for non-Context derived classes, free functions etc
struct Base {
    static Log& GetLog()
    {
        return gLog;
    }
};

// classes derive from this to add context specific information when logging
template <typename Self>
struct Context
{
    // this GetLog adds prefix to Context derived classes
    Log& GetLog()
    {
        static_cast<const Self*>(this)->Token(gLog); // add the Context's Token to the log
        return gLog << ": ";
    }
};

//-------------------------

template<typename T>
struct Foo : Context<Foo<T>>
{
    typedef Context<Foo<T>> Base;
    
    void Func1()
    {
        LOG << __func__ << std::endl;
    }
    void Func2()
    {
        LOG << __func__ << std::endl;
    }

    Log& Token(Log& l) const { return l << "Foo"; }
};

// logging inside a non-Context derived class
struct Bar
{
    void Func()
    {
        LOG << __func__ << std::endl;
    }
};

// logging inside a free function 
void Baz()
{
    LOG << __func__ << std::endl;
}

//-------------------------

int main()
{
    Foo<int> f;
    f.Func1();
    f.Func2();

    Bar b;
    b.Func();

    Baz();

    return 0;
}