Simple C++ Logger

I have had the need for a simple logger for my C++ programs for a long time. About a year ago I decided to write my own.

Requirements

I had the following requirements for my logger:

  1. Cross platform (Windows, Linux)
  2. Run-time control of logging level
  3. Multiple parallel output streams, each with a specific log level
  4. File and  std::ostreambackends
  5. Possibility of extending the types of output streams

Basic design

In order to achieve this I came up with the following structure:

  • A generic interface for a backend
  • A singleton Logger class with methods to add backends
  • Convenient macro definitions

Some detail

Here is a bit more detail to the above description. Firstly the generic backend interface along with the file backend implementation. Secondly the Logger singleton and finally the macro’s.

Generic backend interface

The generic interface utilizes a  std::ostream as the lowest level of logging, which already provides flushing capability. A mutex is utilized which allows multiple threads to safely write to a backend concurrently. The interface itself does not offer much functionality.

File backend implementation

Because the C++ standard  libraries already provide a cross-platform solution, it is used for the file handling. The file backend basically just uses polymorphism on the m_streammember variable of the base class to provide an output file stream.

Logger singleton

Quite a mouthful, but I will attempt to describe what is lacking in the comments (there are no comments). The singleton behavior is established in the following static method:

The static object is created on the first call to the  static Logger *instance()method and subsequent calls will return the already existing singleton object. Note, however, that this method is not re-entrant until the object is created the first time and after the object is destroyed (which should not happen during run-time).

The method  Logger::AddLogFileOutputStream(string ...) adds a new file backend to the list of backends the logger will use. The rest of the methods are self explanatory.

One method that needs to be discussed is the  Logger::Log method. It takes a severity level, message, file string, line string and function string. These are formatted with  stringstream into a detailed log entry and then streamed into each of the output streams if the severity level is sufficient. Each stream is locked with its mutex for thread safety.

Macro helpers

These macros define a shorthand for all the severity types. A special debug shorthand is provided which is also defined to blank no-op when compiling with a release build.

One macro to make note of is  MYLOG_N which uses some macro magic to log every nth time the macro is called.

Usage examples

Here are some usage examples:

Conclusion

The code is available at GitHub under the LGPL 3 license. Further work could be done to handle file size limits or log file rotation. Adding a syslog backend could also prove useful.

I hope this post is informative and that the code and idea is of use.

Leave a Comment