Search code examples
c++singletonglobal-variablesshared-ptr

Making sure global logger object lives longer than other global objects


I have an application with some singleton objects. They want to write log messages at construction and destruction, which happens at the initialization of global variables. Now the logging provider is also a singleton object like this:

// loggingProvider.h
class LoggingProvider {
    static boost::shared_ptr<LoggingProvider> instance;
public:
    static boost::shared_ptr<LoggingProvider> getInstance();
    /* ... */
};

// loggingProvider.cpp
boost::shared_ptr<LoggingProvider> LoggingProvider::instance;

boost::shared_ptr<LoggingProvider> getInstance() {
    if (!instance) {
        instance.reset(new LoggingProvider());
    }
    return instance;
}

The logger interface looks like this:

// logger.h
class Logger {
    const boost::shared_ptr<LoggingProvider> provider;
    const std::string prefix;
public:
    Logger(const std::string prefix);
    /* ... */
}

// logger.cpp
Logger::Logger(const std::string& prefix) :
        provider(LoggingProvider::getInstance()), prefix(prefix) {}

The idea is to be able to declare loggers as global variables in multiple cpp files and be sure that the logging provider is destroyed after all loggers are destroyed, because it is managed by boost::shared_ptr. If I declare a logger like this at the top of a file, it is guaranteed to be destructed after any singleton object in the same file:

// somewhere.cpp
static Logger logger("Somewhere");

Sadly, this code doesn't work. When I debug it, the instance of LoggingProvider is created multiple times. I believe this is the case because the instance field is actually initialized after some of the logger declarations. Now I know there is no way no control the initialization of global variables across files, so is there another way to get this to work?


Solution

  • Just use the classical singleton idiom. You don't want shared_ptr here, because you don't want to destruct the object, ever. Basically, something like:

    class LoggingProvider
    {
        static LoggingProvider* our_instance;
    public:
        static LoggingProvider& instance();
        //  ...
    };
    
    LoggingProvider* LoggingProvider::our_instance
            = &LoggingProvider::instance();
    
    LoggingProvider&
    LoggingProvider::instance()
    {
        if ( our_instance == NULL ) {
            our_instance = new LoggingProvider;
        }
        return *our_instance;
    }
    

    The important things here are 1) the pointer has no non-trivial constructor, and 2) the instance is never destructed.

    One thing: since any file you're outputting to will never be closed, make sure you flush all output. (I usually do this by using short lived, temporary instances of the actual logger, which acquire the target streambuf from the LoggerProvider, and flush it in their destructor.)