Macros - how, what for?

Simply put, you need to use macros to make sure objects (logger(s) and filter(s)) :

The problem we want to avoid is using a logger object before it's initialized - this could happen if logging from the constructor of a global/static object.

Using macros makes sure logging happens efficiently. Basically what you want to achieve is something similar to:

if ( is_filter_enabled) 
    logger.gather_the_message_and_log_it();

The if-else strategy

When gathering the message, what the macros will achieve is this:

#define YOUR_COOL_MACRO_GOOD if ( !is_filter_enabled) ; else logger.gather_the_message_and_log_it();

The above is the correct way, instead of

#define YOUR_COOL_MACRO_BAD if ( is_filter_enabled) logger.gather_the_message_and_log_it();

because of

if ( some_test)
  YOUR_COOL_MACRO_BAD << "some message ";
else
  whatever();

In this case, whatever() will be called if some_test is true, and if is_filter_enabled is false.



Using the macros supplied with the library

There are several types of macros that this library supplies. They're explained below:

Macros to declare/define logs/filters

BOOST_DECLARE_LOG - declaring a log

BOOST_DECLARE_LOG(log_name, logger_type)

This declares a log. It should be used in a header file, to declare the log. Note that logger_type only needs to be a declaration (a typedef, for instance)

Example:

typedef logger_format_write< > logger_type;
BOOST_DECLARE_LOG(g_l, logger_type) 

BOOST_DEFINE_LOG - defining a log

BOOST_DEFINE_LOG(log_name, logger_type)

This defines a log. It should be used in a source file, to define the log.

Example:

typedef logger_format_write< > logger_type;
...
BOOST_DEFINE_LOG(g_l, logger_type) 

BOOST_DEFINE_LOG_WITH_ARGS - defining a log with arguments

BOOST_DEFINE_LOG_WITH_ARGS (log_name, logger_type, args)

This defines a log - and specifies some arguments to be used at its constructed. It should be used in a source file, to define the log.

Example:

typedef logger< default_, destination::file> err_log_type;
...
BOOST_DEFINE_LOG_WITH_ARGS( g_log_err(), err_log_type, ("err.txt") )

BOOST_DECLARE_LOG_FILTER - declaring a log filter

BOOST_DECLARE_LOG_FILTER(filter_name, filter_type)

This declares a log filter. It should be used in a header file, to declare the log filter.

Example:

BOOST_DECLARE_LOG_FILTER(g_log_filter, filter::no_ts )

BOOST_DEFINE_LOG_FILTER - defining a log filter

BOOST_DEFINE_LOG_FILTER(filter_name, filter_type)

This defines a log filter. It should be used in a source file, to define the log filter.

Example:

BOOST_DEFINE_LOG_FILTER(g_log_filter, filter::no_ts )

BOOST_DEFINE_LOG_FILTER_WITH_ARGS - defining a log filter with args

BOOST_DEFINE_LOG_FILTER_WITH_ARGS(filter_name, filter_type, args)

This defines a log filter - and specifies some arguments to be used at its constructed. It should be used in a source file, to define the log filter.

Example:

#define L_ BOOST_DEFINE_LOG_FILTER(g_log_filter, filter::no_ts )

Macros that help you define your own macros for logging

BOOST_LOG_USE_LOG_IF_LEVEL

Uses a logger if a filter has a certain level enabled:

BOOST_LOG_USE_LOG_IF_LEVEL(log, level_filter, level )

Example:

BOOST_DECLARE_LOG_FILTER(g_log_level, boost::logging::level::holder ) 
BOOST_DECLARE_LOG(g_log_err, logger_type) 

#define LERR_ BOOST_LOG_USE_LOG_IF_LEVEL(g_log_err(), g_log_level(), error )

See Defining macros to do logging for more details

BOOST_LOG_USE_LOG_IF_FILTER

Uses a logger if a filter is enabled:

BOOST_LOG_USE_LOG_IF_FILTER(log, filter_is_enabled)

Example:

#define LERR_ BOOST_LOG_USE_LOG_IF_FILTER(g_log_err(), g_log_filter()->is_enabled() )

See Defining macros to do logging for more details

BOOST_LOG_USE_LOG

Uses a logger:

BOOST_LOG_USE_LOG(l, do_func, is_log_enabled)

Normally you don't use this directly. You use BOOST_LOG_USE_LOG_IF_FILTER or BOOST_LOG_USE_LOG_IF_LEVEL instead.

See Defining macros to do logging for more details

BOOST_LOG_USE_SIMPLE_LOG_IF_FILTER

Uses a simple logger:

BOOST_LOG_USE_SIMPLE_LOG_IF_FILTER(l, is_log_enabled)

A simple logger is one that uses a simple gather class (FIXME). Example:

struct no_gather {
    const char * m_msg;
    no_gather() : m_msg(0) {}
    const char * msg() const { return m_msg; }
    void out(const char* msg) { m_msg = msg; }
    void out(const std::string& msg) { m_msg = msg.c_str(); }
};

typedef logger< no_gather, destination::cout > app_log_type;

#define LAPP_ BOOST_LOG_USE_SIMPLE_LOG_IF_FILTER(g_log_app(), g_log_filter()->is_enabled() ) 

See Defining macros to do logging for more details



Setting formatter/destination strings

BOOST_LOG_FORMAT_MSG

Sets the string class used by the formatter classes. By default, it's std::(w)string

BOOST_LOG_FORMAT_MSG( string_class )

You can do this to optimize formatting the message - that is, use a string class optimized for appending and prepending messages (which is basically what formatting is all about).

Example:

BOOST_LOG_FORMAT_MSG( optimize::cache_string_one_str<> )

BOOST_LOG_DESTINATION_MSG

Sets the string class used by the destination classes. By default, it's std::(w)string

BOOST_LOG_DESTINATION_MSG( string_class )

Example:

BOOST_LOG_DESTINATION_MSG( std::string )

Usually you won't need to change this. The destination classes don't change the contets of the string - each class just writes the string to a given destination.



Using tags

Note that tags are only used when you create your own macros for logging. See the tag namespace.

BOOST_LOG_TAG

BOOST_LOG_TAG(tag_class)

Adds a tag from the boost::logging::tag namespace. In other words, this is a shortcut for boost::logging::tag::tag_class. Note that in case the tag_class has a custom constructor, you need to pass the params as well, after the macro, like shown below.

Example:

#define L_(module_name) BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled() ) .set_tag( BOOST_LOG_TAG(module)(module_name) )

BOOST_LOG_TAG_LEVEL

Adds a level tag.

BOOST_LOG_TAG(tag_level)

Example:

#define LDBG_ BOOST_LOG_USE_LOG_IF_LEVEL(g_log_dbg(), g_log_level(), debug ) .set_tag( BOOST_LOG_TAG_LEVEL(debug) )
#define LERR_ BOOST_LOG_USE_LOG_IF_LEVEL(g_log_dbg(), g_log_level(), error ) .set_tag( BOOST_LOG_TAG_LEVEL(error) )

BOOST_LOG_TAG_FILELINE

Ads the file/line tag (that is, the current __FILE__ and __LINE__ will be appended, for each logged message).

BOOST_LOG_TAG_FILELINE

Example:

#define L_ BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled() ) .set_tag( BOOST_LOG_TAG_FILELINE)

BOOST_LOG_TAG_FUNCTION

Ads the function tag (that is, the BOOST_CURRENT_FUNCTION will be appended, for each logged message).

BOOST_LOG_TAG_FUNCTION

Example:

#define L_ BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled() ) .set_tag( BOOST_LOG_TAG_FUNCTION)



Macros that treat compilation time

Assume you're using formatters and destinations, and you include <boost/logging/format.hpp> in every source file when you want to do logging. This will increase compilation time quite a bit (30 to 50%, in my tests; depending on your application' complexity, this could go higher).

Thus, you can choose to:

  1. have fast compilation time, and a virtual function call per each logged message (default on debug mode)
  2. have everything inline (no virtual function calls), very fast, and slower compilation (default on release mode)

In the former case, most of the time you won't notice the extra virtual function call, and the compilation time will be faster.


Fast Compilation time


Slow Compilation time


Compile time sample (and results)

Recently I created a sample (compile_time) to test the effect of BOOST_LOG_COMPILE_FAST_ON. The results were not as promising as I had hoped. However, still, when BOOST_LOG_COMPILE_FAST_ON is on, will compile faster by 30-40%. Noting that this is just an simple example, the results might not be that conclusive. Anyway, here they are:

Tested on 16 jan 2008/intel core duo 2.16Ghz machine, 5400Rpm HDD

If you have other results, or results from a big program using Boost Logging, please share them with me. Thanks!



Macros that deal with Thread Specific Storage

These are the macros that specify what implementation of TSS (Thread Specific Storage) we will be using. Note that I did my best to remove the dependency on boost::thread - the only dependence left is when you use use a logger that writes everything on a dedicated thread.

By default, for TSS, we use the internal implementation (no dependency).

The possibilities are:

BOOST_LOG_TSS_USE_INTERNAL

If defined, it uses our internal implementation for TSS

BOOST_LOG_TSS_USE_BOOST

If defined, it uses the boost::thread's implementation for TSS

BOOST_LOG_TSS_USE_CUSTOM

If defined, it uses a custom implementation for TSS. The interface of this implementation should match boost::thread's interface of thread_specific_ptr class.

Your class should have this interface:

template <typename T> class my_thread_specific_ptr ;

When defining BOOST_LOG_TSS_USE_CUSTOM, do it like this:

#define BOOST_LOG_TSS_USE_CUSTOM = my_thread_specific_ptr

BOOST_LOG_NO_TSS

If defined, we don't use TSS as all.

Copyright John Torjo © 2007
Have a question/ suggestion/ comment? Send me feedback