C++ Logging

It seems that logging is something we do in about every program we write. Logging complements the standard output with richer messages and detailed information. And each time it seems like we’re asking ourselves how to do that exactly.

Of course, there are already existing logging frameworks out there, but I was wondering how much work it implied. I wanted something that would integrate seamlessly to the existing C++ streams: it had to behave exactly as a classical ostream from the programmer’s point of view, but had to insert timestamps and manage message priorities. The simplest way to do so is probably to have a class that inherits from ostream or similar and that already overloads all the operator<<s.

But instead of deriving ostream, I decided to derive logstream from ostringstream to use its string buffer: the stream has to buffer text until std::endl is sent to it, where it flushes to file (or cout, depending on which stream you bound the log) but inserts a leading timestamp.

From there it was relatively straightforward. First, I defined a few logging levels (relative to the logstream class):

   quiet,    // no output
   error,    // only errors are output
   warning,  // errors and warnings are output
   info,     // errors, warnings, and informative messages
   all,      // all messages are output
   none,     // end of enum marker
  } log_level;

Of course, <iomanip>-style stream manipulator are created to change temporarily the logging level (as temporary as std::setw); it is also possible to send directly a log_level to the stream to the same effect. You can also change the current maximum logging level by invoking logstream::change_log_level to change it permanently (until the next invocation of logstream::change_log_level, that is).

For everything else, it should behave as a standard std::ostream, having inherited all the operator<< from its base class.

* *

The implementation is straightforward but uses two “tricks”. The first is to bind a custom std::endl to the logstream‘s flush function. It basically consists in overloading std::endl to accommodate a logstream (and, yes, putting the overload in std:: namespace). The logstream::flush function generates a timestamp, outputs it, then outputs its buffer. The buffer is then reset.

The second “trick” is to inherit all the operator<<s from the base class, despite their not being virtual:

// inherits EVERYTHING that's
// not explicitly overloaded
// in this class
template <typename T>
inline logstream & operator<<( const T & t )
  (*(std::ostringstream*)this) << t;
  return *this;

* *

The look of the log is currently similar to:

2010/10/17 18:27:35.731489 warning: something happened
2010/10/17 18:27:35.732183 error: could not X on Y

(Yes, these are trainling microseconds.) But can easily be customized by changing the flush to do the pretty printing you want.

* *

There’s still a couple of things to do, such as inserting the level of the message just after the timestamp, which doesn’t need much to do.

What lacks totally is higher-level structure. The current implementation does nothing to help you group messages in “contexts”; something that would help you identify exactly what messages are grouped with or caused by, or related to, other messages. That’s on the TO DO list, I guess.

* *

You can download the whole code here.

One Response to C++ Logging

  1. […] you remembered: I did something, maybe a little more complicated, quite similar a while ago. There also, the idea was to provide a familiar abstraction with slightly modified […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: