Building a lightweight, thread-safe logger that can be redirected to different I/O channels.
In this article, I want to discuss the importance of providing a logging component in your software project. From a very general point of view, any software can be developed in a way that allows the running code to provide a trace of its execution in a log file, then depending on the life phase of the product (development or production), it may be released with a different level of logging set. Having a logger becomes crucial when debugging is not possible or is inconvenient. Under some circumstances, debugging is not possible–for example, because the application runs on specific hardware or the problem is completely unclear. In those cases, collection and analysis of logs is crucial to pinpointing a problem.
At Nokia Siemens Networks, I developed the firmware running on NSN's Base Transceiver Station (BTS). A BTS is very complex hardware driven by impressive firmware. When something does not work (especially in a real network), there is no way to check where the problem traces back other than reading the BTS logs, a common logging platform every subsystem shares with all the other components, which provides prints on different severity levels.
A logger should be effective, efficient, and able to provide clear data. Most importantly, it has to guarantee proper functioning even when the whole system crashes. It makes no sense to write logs if the logger stops running at every crash. The logging platform, like the captain of a sinking ship, must “survive” until the end.
In this article, I develop a small logger that provides two levels of logging and three types of log severity (Error, Warning, and Debug). The code has been used in different projects of different sizes, is thread-safe, computationally lightweight, and easily customizable to work with different log output (to allowing remote logging for example).
Dr. Dobb's has previously published another implementation of a good C/C++ logger (see Logging In C++ and Logging In C++: Part 2). There are few important differences between what you'll see here and that solution. First and most important, the logger discussed here will flush the log messages immediately and will not buffer them, which is crucial when a serious destabilizing event occurs. (The implementation provided in Logging In C++: Part 2 provides the ability to enable or disable a single log print at runtime. The way this feature is implemented is very clever and I encourage you all to take a look at both articles.) Another difference in my implementation is that I use only C++ and STL functionalities (no Boost libraries). To understand the code presented, I expect that you'll be familiar with the variadic template concept of the new C++ standard.
This article continues on our sister web site, Dr. Dobb's .