How do I implement convenient logging without a Singleton?

First: the use of std::unique_ptr is unnecessary:

void Log::LogMsg(std::string const& s) {
  static Log L;
  L.log(s);
}

Produces exactly the same lazy initialization and cleanup semantics without introducing all the syntax noise (and redundant test).

Now that is out of the way…

Your class is extremely simple. You might want to build a slightly more complicated version, typical requirements for log messages are:

  • timestamp
  • level
  • file
  • line
  • function
  • process name / thread id (if relevant)

on top of the message itself.

As such, it is perfectly conceivable to have several objects with different parameters:

// LogSink is a backend consuming preformatted messages
// there can be several different instances depending on where
// to send the data
class Logger {
public:
  Logger(Level l, LogSink& ls);

  void operator()(std::string const& message,
                  char const* function,
                  char const* file,
                  int line);

private:
  Level _level;
  LogSink& _sink;
};

And you usually wrap the access inside a macro for convenience:

#define LOG(Logger_, Message_)                  \
  Logger_(                                      \
    static_cast<std::ostringstream&>(           \
      std::ostringstream().flush() << Message_  \
    ).str(),                                    \
    __FUNCTION__,                               \
    __FILE__,                                   \
    __LINE__                                    \
  );

Now, we can create a simple verbose logger:

Logger& Debug() {
  static Logger logger(Level::Debug, Console);
  return logger;
}

#ifdef NDEBUG
#  define LOG_DEBUG(_) do {} while(0)
#else
#  define LOG_DEBUG(Message_) LOG(Debug(), Message_)
#endif

And use it conveniently:

int foo(int a, int b) {
  int result = a + b;

  LOG_DEBUG("a = " << a << ", b = " << b << " --> result = " << result)
  return result;
}

The purpose of this rant ? Not all that is a global need be unique. The uniqueness of Singletons is generally useless.

Note: if the bit of magic involving std::ostringstream scares you, this is normal, see this question

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)