Channels ▼
RSS

Security

A Lightweight Logger for C++


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.

Listing Four: The file log policy implementation.

void file_log_policy::open_ostream(const std::string& name)
{
   out_stream->open( name.c_str(), std::ios_base::binary|std::ios_base::out );
   if( !out_stream->is_open() ) 
   {
        throw(std::runtime_error("LOGGER: Unable to open an output stream"));
   }
}

void file_log_policy::close_ostream()
{
    if( out_stream )
    {
        out_stream->close();
    }
}

void file_log_policy::write(const std::string& msg)
{
    (*out_stream)<<msg<<std::endl;
}

file_log_policy::~file_log_policy()
{
    if( out_stream )
    {
        close_ostream();
    }
}

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.

Listing Five: The print function.

template< typename log_policy >
    template< severity_type severity , typename...Args >
void logger< log_policy >::print( Args...args )
{
    write_mutex.lock();
    switch( severity )
    {
        case severity_type::debug:
             log_stream<<"<DEBUG> :";
             break;
        case severity_type::warning:
             log_stream<<"<WARNING> :";
             break;
        case severity_type::error:
             log_stream<<"<ERROR> :";
             break;
    };
    print_impl( args... );
    write_mutex.unlock();
}

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.

Listing Seven: print_impl implementation.

template< typename log_policy >
void logger< log_policy >::print_impl()
{
    policy->write( get_logline_header() + log_stream.str() );
    log_stream.str("");
}

template< typename log_policy >
    template<typename First, typename...Rest >
void logger< log_policy >::print_impl(First parm1, Rest...parm)
{
    log_stream<<parm1;
    print_impl(parm...);	
}

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.


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