Caching messages before logs are initialized

Caching - why is it needed?

Logging is all fine and dandy, but what if you do some logging before the actual logs are initialized? It's quite easy to end up doing this:

You could even run into more complicated scenarios, where you create other threads, which, until you initialize your logs, do some logging. It's good practice to log a thread's begin and end, for instance.

Even though you think this'll never happen to you, usage of singletons and other static variables is quite common, so better to guard against it.

One solution would be for the library to rely on an external function, like void boost::logging::init_logs(), and have your application have to define it, and it its body, initialize the logs. The library would then make sure the init_logs() is called before any log is used.

There are several problems with this solution:

Thus, I came up with a caching mechanism. You can choose to:

By default, for each log, cache is turned on. To turn cache off (mark the log as initialized), just call mark_as_initialized() on it. You'll see that I'm doing this on all examples that come with the library.

Cache messages before logs are initialized regardless of their filter (BOOST_LOG_BEFORE_INIT_LOG_ALL)

This case is the default. When cache is on, all messages are cached, regardless of their filter (as if all filters are turned on). Then, when cache is marked as initialized, all cached messages are logged.

If you want to force this setting, make sure you define the BOOST_LOG_BEFORE_INIT_LOG_ALL globally (it's on by default anyway).

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

L_ << "this message will be logged, even if filter will be turned off";

Cache messages before logs are initialized/ cache their filter as well (BOOST_LOG_BEFORE_INIT_CACHE_FILTER)

It's a bit inefficient (after invoking the filter, it will always ask if cache is on or off). Also, it increases the application's size a bit - for each log statement, I will generate a callback that I can call later to see if the filter is still turned on or off.

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

L_ << "this message will not be logged";

If you do want to use this setting, make sure you define the BOOST_LOG_BEFORE_INIT_CACHE_FILTER globally.


If you don't want to cache the filter, just skip to the next section.
If you cache the filter as well, in order for the caching process to work, all the parameters you pass to the filter need to be:

Assume you have a logger with a filter based on levels:

// for exposition only - normally you'd use BOOST_LOG_USE_LOG_IF_LEVEL
#define L_(lvl) BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_level()->is_enabled( lvl ) )

If you cache the filter, the expression g_log_level()->is_enabled( lvl ) needs to be recomputed at a later time (when the log is marked as initialized, and all messages that were cached, are logged). Thus, all parameters that are passed to the your L_ macro need to be either compile-time constants or global values. Otherwise, a compile-time error will be issued:

void f() {
  boost::logging::level lvl = ...;
  // will generate a compile-time error : using a local variable as param
  L_(lvl) << "wish it could work";

Normally you should not care about this, since whatever you pass to your logging macros should indeed be constant.

Ignore all messages before mark_as_initialized (BOOST_LOG_BEFORE_INIT_IGNORE_BEFORE_INIT)

In the last case, all messages before mark_as_initialized() are ignored.

If you do want to use this setting, make sure you define the BOOST_LOG_BEFORE_INIT_IGNORE_BEFORE_INIT globally.

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

L_ << "this message will NOT be logged";

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