c++boostboost-log

Building boost::log::formatter on runtime condition


I want to build boost::log::formatter conditionally, however, formatter objects do not seem to behave as 'normal values'.

For example:

auto sink = boost::log::add_console_log(std::clog);

auto format =
    expr::stream << expr::format_date_time(timestamp, "%Y-%m-%d %H:%M:%S.%f");

boost::log::formatter formatter = format;

if (condition) {

    auto t = "msg=" << expr::smessage;

    formatter <<= t;
}

sink->set_formatter(formatter);

However, operator<<= (or even operator= for that matter) doesn't seem to modify formatter object which is very surprising for me!

What is the proper way of doing this?


Solution

  • Formatting streaming expression constructs a function object that will be executed when a log record is formatted into string. The function object type is final in the sense that you cannot modify it by adding new formatting statements to it. In fact, any streaming statement effectively creates a new formatter function object of a distinct type. boost::log::formatter is just a type erasure helper that encapsulates the formatter function object, similar to how std::function encapsulates a lambda function that you assign to it.

    If you want to modify the formatter, you must create a new formatter that will have the original formatter as its left part and the added streaming statements on the right. This new formatter can then be assigned to boost::log::formatter.

    auto sink = boost::log::add_console_log(std::clog);
    
    boost::log::formatter formatter;
    
    auto common_format =
        expr::stream << expr::format_date_time(timestamp, "%Y-%m-%d %H:%M:%S.%f");
    
    if (condition) {
        auto format = common_format << "msg=" << expr::smessage;
        formatter = format;
    } else {
        formatter = common_format;
    }
    
    sink->set_formatter(formatter);
    

    Note that this will only work with streaming expressions. This will not work with Boost.Format-style formatters based on boost::log::expressions::format.

    Also note that condition will be evaluated once - when the formatter is constructed, not when it is executed to format log records. If you want to test a condition when the formatter is executed then you should look into conditional formatters.