Archive for April 21st, 2009

Using C++ STL streambuf/ostream to create time-stamped logging class

April 21, 2009

I’ve seen many people asking around on forums about a way to automatically append a timestamp in the beginning of a text line, in order to aid in debugging or logging activities. Here’s a way ti do it.

First, the credits: the code is heavily based on this example by Nicolai M. Josuttis.

It took me a considerable amount of time to get this working, and I found that the main reason for that was my lack of understaning about the streambuff and ostream STL classes. So, do yourself a favor and read:
http://www.cplusplus.com/reference/iostream/streambuf/ and
http://www.cplusplus.com/reference/iostream/ostream/
before moving on.

Finally, here’s the code:

#include <string>
#include <iostream>
#include <streambuf>
using namespace std;

// This is the streambuffer; its function is to store formatted data and send
// it to a character output when solicited (sync/overflow methods) . You do not
// instantiate it by yourself on your application; it will be automatically used
// by an actual output stream (like the TimestampLoggerStream class defined ahead)
class TimestampLoggerStreambuf : public streambuf
{
protected:
  static const int bufferSize = 10;   // size of data buffer
  char buffer[bufferSize];            // data buffer

public:
    TimestampLoggerStreambuf() { setp (buffer, buffer+(bufferSize-1)); }
    virtual ~TimestampLoggerStreambuf() { sync(); }

protected:
    string LineHeader() {
        time_t secondsSinceEpoch = time(NULL);
        tm* brokenTime = localtime(&secondsSinceEpoch);
        char buf[80];
        strftime(buf, sizeof(buf), "[%d/%m/%y,%T] ", brokenTime);
        return string(buf);
    }
    // flush the characters in the buffer
    int flushBuffer () {
        int num = pptr()-pbase();
        if (write(1, buffer, num) != num) {
            return EOF;
        }
        pbump(-num); // reset put pointer accordingly
        return num;
    }
    virtual int overflow ( int c = EOF ) {
        if (c != EOF) {
            *pptr() = c;    // insert character into the buffer
            pbump(1);
        }
        if (flushBuffer() == EOF)
            return EOF;
        return c;
    }
    virtual int sync() {
        cout << LineHeader() << flush;
        if (flushBuffer() == EOF)
            return -1;    // ERROR
        return 0;
    }
};

// This is the output stream; its function is to format data (using mainly the <<
// operator) and send it to a streambuf to be stored and written to the output.
class TimestampLoggerStream : public ostream
{
public:
    TimestampLoggerStream() : ostream(new TimestampLoggerStreambuf()), ios(0) {}
    ~TimestampLoggerStream() { delete rdbuf(); }
};

// Demo main() function, simply write a few lines to standard output. The endl
// manipulator indicates the end of a text line and requests a flush. This will
// trigger a call to TimestampLoggerStreambuf::sync() which will output the
// timestamp header followed by the line of text.
int main(int argc, char *argv[])
{
    cout << "Application started." << endl;

    TimestampLoggerStream ls;

    ls << "abc";
    ls << "def" << endl;
    ls << "123" << endl;

    return 0;
}

The output should be:

Application started.
[21/04/09,18:08:37] abcdef
[21/04/09,18:08:37] 123

Update: if you’re interested in a logger class that writes simoultaneously to the disk and to stdout, please check this other post.

This is it for this topic. If this blog helps you save a little time, please leave a comment and I’ll log it in for an estimate of time saved x time spent maintaining this blog.


Follow

Get every new post delivered to your Inbox.