AlexandruIrimiea AlexandruIrimiea - 2 months ago 24
C++ Question

Boost Log cut long log messages

Is there a possibility in

to consider only a part of the log message if it is too long (e.g. 1000 characters)? This would be useful when tracing variable content, where the entirety of it is not mandatory to figure out the information needed.

E.g.: when printing a string with the file list from the current directory, I don't need to see the entire list to know if the filesystem was scanned successfully.

boost::log::add_file_log
(
boost::log::keywords::file_name = "logs/Log_%Y-%m-%d_%H-%M-%S.log",
boost::log::keywords::rotation_size = 10 * 1024 * 1024,
boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
boost::log::keywords::auto_flush = true,
boost::log::keywords::format =
(
boost::log::expressions::stream
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S:%f")
<< " [" << boost::log::expressions::attr<boost::log::attributes::current_thread_id::value_type>("ThreadID") << "]"
<< ": <" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage
)
);


E.g.: can
boost::log::expressions::smessage
be customized like
boost::log::expressions::smessage::substr(0, 1000)
or in any other way?

Answer

In Boost 1.62 there will be a max_size_decor decorator that does what you're asking. You would use it like this:

boost::log::add_file_log
(
    boost::log::keywords::file_name = "logs/Log_%Y-%m-%d_%H-%M-%S.log",
    boost::log::keywords::rotation_size = 10 * 1024 * 1024,
    boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
    boost::log::keywords::auto_flush = true,
    boost::log::keywords::format =
    (
        boost::log::expressions::stream
        << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S:%f")
        << " [" << boost::log::expressions::attr<boost::log::attributes::current_thread_id::value_type>("ThreadID") << "]"
        << ": <" << boost::log::trivial::severity << "> " 
        << boost::log::expressions::max_size_decor(1000)
        [
            boost::log::expressions::stream << boost::log::expressions::smessage
        ]
    )
);

The decorator would limit the output of the adopted formatter to the specified number of characters.

In the meantime you can create your own formatter. There is this answer describing multiple ways of doing that. For example, you could use boost::phoenix::bind to wrap your function that would do the size limiting:

boost::string_view limit_size(boost::log::value_ref<
    std::string, boost::log::expressions::tag::smessage > const& message)
{
    if (!message)
    {
        // No message attribute in the log record
        return boost::string_view();
    }

    boost::string_view msg = message.get();
    return msg.substr(0, 1000);
}

boost::log::add_file_log
(
    boost::log::keywords::file_name = "logs/Log_%Y-%m-%d_%H-%M-%S.log",
    boost::log::keywords::rotation_size = 10 * 1024 * 1024,
    boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
    boost::log::keywords::auto_flush = true,
    boost::log::keywords::format =
    (
        boost::log::expressions::stream
        << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S:%f")
        << " [" << boost::log::expressions::attr<boost::log::attributes::current_thread_id::value_type>("ThreadID") << "]"
        << ": <" << boost::log::trivial::severity << "> " 
        << boost::phoenix::bind(&limit_size, boost::log::expressions::smessage.or_none())
    )
);

In this example, phoenix::bind creates a wrapper function object that extracts the message attribute value from the log record and passes it into your limit_size function wrapped in the value_ref reference wrapper. If the log record does not contain a message the reference wrapper is empty (that's what or_none does). Whatever limit_size returns will be output into the stream as part of the formatting process. In this case you can use boost::string_view or boost::string_ref to avoid copying the string.

Comments