Search code examples
c++boostboost-log

Boost Log cut long log messages


Is there a possibility in boost::log 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?


Solution

  • Since Boost 1.62 there is a max_size_decor decorator that does what you're asking. You can 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 limits the output of the adopted formatter to the specified number of characters.

    You can also 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.