Channels ▼
RSS

Tools

A Highly Configurable Logging Framework In C++


Extending the Logging Framework

Due to its high configurability, it is possible to extend the framework (Requirement #2) and I've already shown one extension for the back-end side. Now, I will explain customizing the front-end — first, using customized prefixes and second, beautifying the output with colors.

Let's start with prefixed log statements, and where such prefixes are worthwhile. If you are a developer of a special algorithm termed "XYZ" and you would like to debug it with easy recognizable messages, an option you have is prefixing each debug message with a special string. To do that with my framework, you have to write a simple struct, holding the logging level interface:


struct LogAlgorithmXYZ {
    static Level::levels level () {
        return Level::debug;
    }

    static const char * desc() {
        return "[ AlgorithmXYZ ]";
    }
};

By providing a lot of such structs with different levels (debug, warning, info, etc) and descriptions, you can distinguish between different phases of the algorithm. If you finish debugging and want keep the statements there for later debugging sessions and/or documentation purposes, the logging level interface lets you selectively disable, thus in release builds none of the debugging messages are contained.

Enhancing the framework by additional logging levels is more like parameterization the framework than a real extension. The secondly promised extension — coloration — is from another category. Enabling beautified output requires an enhanced loggingReturnType, which is the topic of the following paragraphs.

Here, I describe the coloration for POSIX-conforming terminals, using ANSI escape sequences. First, you have to tell the framework that you want to extend the output type by your own, disabling the default definition of the loggingReturnType:


#define LOGGING_DEFINE_EXTENDED_OUTPUT_TYPE
#include "logging/logging.h"

Then I define a struct ConsoleColors containing an enumeration of ANSI color values. This struct lets me to have a catchable type for a special overloaded operator<<()that does the hard work.


struct ConsoleColors {
    // define some ANSI console values
    enum Colors {
        blue = 34,
        magenta = 35,
        reset = 0
    };
};

After defining the color type, I build a component that is capable to interpret colors and produce the correct escape sequences. The defined Colorizer component is derived from a type given by the template parameter.

As template argument, a configured output type like StdLogType is applicable, allowing the use of all framework core functionality, which is under the hood. The Colorizer provides an operator<<() that catches the color type. The operator<<()first saves the current set converting base, then produces the right escape sequence according to the given color, and finally restores the converting base. Saving and restoring the converting base is needed, due to the switch to decimal output used within the operator method itself. In addition to operator<<(), an additional operator<<()with template is used to forward all untreated types to the base class, that should be able to process them.


template < typename Base>
struct Colorizer : public Base {
    // catch color type and produce correct escape sequence
    Colorizer& operator << (const ConsoleColors::Colors l) {
        unsigned char tmp=Base::getBase();
        *this << log::dec
              << "\033["
              << static_cast<unsigned short>(l)
              << 'm';
        Base::setBase(tmp);
        return *this;
    }

    // forward unknown types to base for further processing
    template<typename T>
    Colorizer& operator << (const T &t) {
        Base::operator<<(t);
        return *this;
    }
};


Having the color type and the processing component ready, one step remains — setting up loggingReturnType. We again use the known setup macro:


LOGGING_DEFINE_OUTPUT( Colorizer<StdLogType > )

As template argument for our new Colorizer extension, I use the former defined StdLogType, which outputs all logging content to std::clog. After this, I can use the extension to color the output:


log::emit() <<ConsoleColors::blue
            <<"Hello World in blue!"
            <<ConsoleColors::reset
            << "and back to normal color mode"
            <<log::endl;

The presented extension is for POSIX-compliant systems, only. If you want to beautify Windows terminals as well, you will need some small adaptations. However, the general strategy of enhancing the framework is still the same. The provided sources contain the presented Colorizer and also a Colorizer example for Windows systems.

Evaluation and Resource Usage

Until now I simply presented my framework and its features, asking you to take my word that it is possible to disable it in parts and use resources efficiently, or disabling it completely, leading to no resource consumption whatsoever. Now, I would like to prove prove that this is possible. To do so, I present two examples comparing resource usage in terms of needed memory.

Example 1: Usage scenario, which uses both flavors of emit()

#include "logging.h"
using namespace ::logging;

int main(int, char**) {
    log::emit() << "Hello World! with the logging framework" <<  log::endl;

    log::emit< Error>()   <<  "Logging an Error"  <<  log::endl;
    log::emit< Trace>()   <<  "Logging a Trace"   <<  log::endl;
    log::emit< Warning>() <<  "Logging a Warning" <<  log::endl;
    log::emit< Info>()    <<  "Logging an Info"   <<  log::endl;
    return 0;
}


In the first usage scenario, Example 1 outputs all messages. For the second scenario, I disable the error level, followed by disabling the trace level in the third scenario until all levels are disabled in the fifth one. After switching all levels off, only the first log statement outputs its message. All tests are compiled by g++ version 4.4.1 for x86 platform with optimization option -Os, leading to size optimized programs. In general, the resource consumption should shrink, which is indeed the case and the results of the size program are depicted in Table 1.

Table 1: Program section size in bytes of the different scenarios


The only remaining thing is to deactivate the framework as a whole, which is feasible with the setting of an additional command-line parameter (-DLOGGING_DISABLE) for the compiler run. For showing that no more resources are needed by the framework itself, I compare the program size of Example 1 with a deactivated logging framework against an empty main implementation; see Example 2.

Example 2: Minimal program used for comparing resource consumption



int main(int,char**) {
    return 0;
}

The results speak for themselves in Table 2. Both the empty main and the program with deactivated logging framework result in the same resource consumption, which proves my statements and the fulfillment of the requirements.

Table 2: Resource consumption of the disabled framework is equivalent to a minimal example program


Conclusion

In this article I presented a logging framework, that is easy to use, portable, extensible, type-safe and enables selective enabling/disabling of levels as well as deactivation as a whole, with the premise of being as resource efficient as possible. One of the great benefits is, that all these properties are realized with standard C++ features, only, thus each standard compliant compiler can be used. I tested the framework with different g++ versions and with Microsoft Visual C++ 9.0.


Michael Schulze is a Ph.D. student at Otto-von-Guericke University, working on adaptable event-based middleware for resource-constraint embedded systems. He can be contacted at mschulze@ivs.cs.ovgu.de.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

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.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video