c++: logger class without globals or singletons or passing it to every method

Does anyone knows if it's possible to have a class like a logger without :

  • using a singleton or a global (a la std::cout)

  • passing an instance/pointer/reference to every method which needs it

I take the example of a logger class, but I have a few classes in my application which would benefit from this (for example, the undo manager).

There are several issues with every solution :

  • using a singleton is problematic for testing (along with the many reasons it's generaly not a good idea to use one) . It's the same with a global. Furthermore, nothing guarantees there will be only ONE instance in the application, and it's not even a requirement (why not have 2 loggers for example ?)

  • passing it to every object constructor (dependency injection), leads to a lot of boilerplate code, and can be error prone because you have to copy/paste the same code a lot of times. Can one seriously consider having a pointer to a Logger in every single class' constructor ???????

So I was wondering if there is a third alternative, in C++, I never heard of ? To me it sounds like it would require some black magic under the hood, but I've been pleasantly surprised by some techniques I learned in stack overflow that I couldn't find in google, so I know there are some real gurus here ;)

Surprisingly, I found many discussions about how to design singletons, or why should one not use singletons, but I couldn't find a post addressing my issue...

Answers


I would imagine you could do something similar to what is done in Java with the Log4j package (and is probably done with the Log4c version of it):

Have a static method which can return multiple logger instances:

Logger someLogger = Logger.getLogger("logger.name");

The getLogger() method isn't returning a singleton object. It's returning the named logger (creating it if necessary). Logger is just an interface (in C++ it could be a totally abstract class) -- the caller doesn't need to know the actual types of the Logger objects being created.

You could continue to mimic Log4j and have an overload of getLogger() that also takes a factory object:

Logger someLogger = Logger.getLogger("logger.name", factory);

That call would use factory to build the logger instance, giving you more control over what underlying Logger objects were being created, likely helping your mocking.

So there's no need to pass anything into the constructors, methods, etc. of your own code. You just grab the desired named Logger when you need it and log to it. Depending on the thread safety of the logging code you write, you could have what getLogger() returns be a static member of your class so you'd only have to call getLogger() once per class.


How about a class with some static methods?

class Logger
{
public:
  static void record(string message)
  {
    static ofstream fout("log");
    fout << message << endl;;
  }
  ...
};

...

void someOtherFunctionSomewhere()
{
  Logger::record("some string");
  ...
}

No Singleton, no global variable, but any code that can see Logger.h can call the member function, and if all of the public member functions return void, it's trivially easy to stub out for testing.


I don't think there is a good alternative in C++, however there is one in Emacs Lisp that might be worth to think about: dynamic binding of variables. The basic concept goes like this, when you refer to a variable, you will access not necessarily the global variable by the name, but the one last defined in the path of execution by the same name. In pseudo-C++ code this would look like this:

// Pseudo-C++ with dynamic binding
Logger logger = Logger("GlobalLogger");

void foo() { logger.log("message"); }

int main() 
{
   foo(); // first
   {
      Logger logger = Logger("MyLogger");
      foo(); // second
   }
   foo(); // third
}

In this pseudo-C++ example when you call foo() the first time, GlobalLogger would be used, when you call it a second time, MyLogger would be called instead, as the MyLogger would override the global variable for as long as it is in scope, even inside foo(). The third call would go back to GlobalLogger as MyLogger dropped out of scope. This allows overriding the global logger with a custom one for selected pieces of code without having to pass a logger object through all the code and without having to set a global variable.

Real C++ doesn't have dynamic binding, but it should be possible to replicate aspects of it:

std::stack<Logger> logger = { Logger("GlobalLogger") };

void foo() { logger.top().log("message"); }

int main()
{
    foo();
    {
      logger.push(Logger("MyLogger"));
      foo();
      logger.pop();
    }
    foo();
}

For further cleanup the stack should be hidden inside a DynamicBinding class, the manual .push()/.pop() operations could be hidden behind a scoped guard and there will be issues with multithreading that will need to be taken care of. But the basic concept might work and give more flexibility then a simple singleton or global variable.


Need Your Help