C/C++

Logging In C++: Part 2

By Petru Marginean, November 20, 2009

Improving log granularity

Setting Logging Level At Line Granularity

The basic idea is to define a local static variable for each logging statement. The variable is used to enable/disable each individual log statement. A Set() function will be defined as a way to change the value of any of the local static variables.

The static variable can have different states:

Uninitialized: The meaning is that the log statement was never executed (yet).

Enabled: The user wants to enable this log statement, no matter what the global log level is.

Disabled: The user wants to disable this log statement, no matter what the global log level is.

enum TEnabler {NOT_INIT = -2, DEFAULT, DISABLE , ENABLE};

Let's define the static variable inside of a for loop like described above. In this way the definition of local static variable is packed in a macro, so users can use it without caring about what it contains. For example:

To read/write the value of the static variable (shared between multiple threads) the Read()/Write() functions are used. They insert memory barriers, so this will make sure the ordering of the code is preserved and the code will work fine even on multiple processor machines. This can be implemented in a portable way by using the Boehm's atomic_ops library.

Also please note that the initialization of the static variable is done (statically) before the for loop is first executed. This avoids a race condition, which would occur if the variable were dynamically initialized.

Having very efficient code when the enabler variable is already initialized (enabler != NOT_INIT) is very important. For that reason locks were avoided on the normal path (DLCP), and there is a possibility that the Init() function above is actually called multiple times for the same variable. Because the Init() function is called just once for each log statement, the locks can be used inside this function, and serialize these calls, without worrying about effciency.

What Init() does is to add the addresses of the static variable into a global map, having the key the file name and the line number identifying the log statement:

The function checks if there are any static variable addresses in the map at the moment (for the file name and line number parameters). If there are any, it will set the value accordingly. If there are no values in the map yet (for example the Set() was called before the log statement at file name/number was first executed), it just saves the value in the map.

The function will insert in the global map the address of the static variable. If the key is not in the map it will use the DEFAULT value. If the key is in the map it will use whatever the value was already set. If Init() is called multiple times with the same static variable address (probably because of a race condition) the code checks if the address is already in the vector, and it ignores the second occurrence. However ignore this situation can be ignored, and accept in this situation having the same address multiple times in the vector. This is a very rare situation and the code will work fine either way anyway.

The Set() and Init() functions are the only ways to change the global map, and both are using the same mutex in to synchronize the access to the map. They are also using the Write() function in order to set the static shared variables, that will insert the appropriate memory barriers.

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!