C/C++

A Lightweight Logger for C++

By Filip Janiszewski, January 31, 2013

Building a lightweight, thread-safe logger that can be redirected to different I/O channels.

The policy interface simply provides three functions to open and close an output stream and a write operation. For the file logging policy, the code will use the C++ std::ofstream to direct the output to the disk. The complete implementation for the file logging policy is visible in the Listing Four.

There's nothing special to comment on here. If the stream-opening operation fails, the function throws a std::runtime_error (line 6). Obviously, a different behavior can be provided.

In this implementation, an exception is thrown exception because the logging facility in my applications is not optional, but crucial (as it is for the firm I work for). In fact, the logging functionalities are seen as features of the software that the customer will use, so if this feature is not working properly, then it is often right to abort the application startup.

The Core Printing Functionality

Every call to a logging macro will be expanded in an invocation of the print function of the logger instance. This function actually does some formatting activities on the log message and use the write function from the used policy to stream out the log string. The Listing Five shows the print function implementation.

Here, you should be familiar with the variadic functions: print is a variadic, which actually accepts as formal argument the parameter pack Args and one more template argument, severity. Actually, severity is an enum, which can be one of three values as shown in Listing Six:

Listing Six: The severity_type enum.

enum severity_type
{
debug = 1,
error,
warning
};

The severity provided as a template parameter is used in the switch statement at line 6 of Listing Five to add the proper severity description to the log message: debug ,warning, or error. The variable log_stream used in the print function implementation is an attribute of the logger class, actually of a std::stringstream type. The first operation at line 5 is a lock request to write_mutex, this is needed to ensure thread safety by guaranteeing that no more than one print operation is performed at the same time. This lock is released at line 19 after the operation has finished. Please note that the locking request is a blocking operation if the mutex is already acquired by a different thread, a wait-free version can use a buffering system to store the log messages until the lock is released. The call to print_impl at line 18 is show next in Listing Seven.

If you're not familiar with the variadic functions, you might not be able to understand why this function has two bodies. It's done this way to access the parameters in the variadic parameter pack. If you look at line 12, you'll see that the function is recursive, and at every call the first argument expanded from parm... is used to, let's say, fill parm1, while all the others are filling the argument pack Rest... .

The value of parm1 is stored at line 11, then the recursion happens again with one parameter less. At a certain point, Rest... will be empty, the last value will be printed and the last call to print_impl performed. If parm... is empty, the recursion is ended by an invocation of the print_impl version that started at line 1. This print_impl version just makes a call to the write function of the logging policy, passing a std::string consisting of a log message preceded by the header, which contains data like the timestamp, the log line number, and so on.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!