main()
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:
init_logs()
has been called - thus, for each log usage, I would need to check if init_logs has been called or not - not very efficientinit_logs()
is called - what if they need some context information - they wouldn't have any context do rely onThus, 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.
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"; g_log_filter()->set_enabled(false); g_l()->mark_as_initialized();
... #define L_ BOOST_LOG_USE_LOG_IF_FILTER(g_l(), g_log_filter()->is_enabled() ) ... L_ << "this message will not be logged"; g_log_filter()->set_enabled(false); g_l()->mark_as_initialized();
If you do want to use this setting, make sure you define the BOOST_LOG_BEFORE_INIT_CACHE_FILTER
globally.
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.
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";
g_l()->mark_as_initialized();