c++boostboost-logboost-logging

Extract boost log attribute values of arbitrary type


There is a logging system with the number of attributes of arbitrary types. The attributes are added by external program(s) using public API (function template). The types aren't known beforehand. The typical way to print out an attribute's value looks like (simplified):

void print(logging::attribute_value const& attr)
{
    auto val = logging::extract<int>(attr);
    if (val) {
        std::cout << "value: " << val.get() << std::endl;
    }
}

In the example above it's already known that the attribute value type is int. What if the expected type isn't known? Of cource, I could write it like:

typedef boost::mpl::vector<int, std::string> types; // Expected types
logging::value_ref<types> val = logging::extract<types>(attr);
[..]

however in that case I have to define all possible types and handle them separately.

Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?

UPDATE

Here are some more details:

// Add new attributes with arbitrary types
template<typename T>
void addProperty(const std::string &key, T && value)
{
    auto attrs = boost::log::core::get()->get_thread_attributes();
    attrs.erase(key);
    attrs.insert(key, boost::log::attributes::make_constant(std::move(value)));
    boost::log::core::get()->set_thread_attributes(attrs);
}

and another function that should print the properties' values

void printProperties()
{
    const auto &attrs = boost::log::core::get()->get_thread_attributes();
    for (auto const &a : attrs) {
        // Print all values. Types aren't known.
    }
}

Solution

  • Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?

    No, Boost.Log doesn't support that, you must know the name and type of the attribute value in order to be able to extract or visit it.

    What you could do is maintain your own mapping between attribute names and formatting routines, such as this:

    std::map<
        logging::attribute_name,
        std::function< void(std::ostream&, logging::attribute_value const&) >
    > formatters;
    
    template< typename T >
    void formatter(std::ostream& strm, logging::attribute_value const& attr)
    {
        auto val = logging::extract< T >(attr);
        if (val)
            strm << val.get();
    }
    
    template<typename T>
    void addProperty(const std::string &key, T && value)
    {
        typedef std::remove_cv_t< std::remove_reference_t< T > > value_type;
        logging::attribute_name name(key);
        formatters[name] = &formatter< value_type >;
        boost::log::core::get()->add_thread_attribute(name,
            boost::log::attributes::make_constant(std::forward< T >(value)));
    }
    
    void printProperties()
    {
        const auto &attrs = boost::log::core::get()->get_thread_attributes();
        for (auto const &a : attrs) {
            auto it = formatters.find(a.first);
            if (it != formatters.end()) {
                std::cout << "value: ";
                it->second(std::cout, a.second.get_value());
                std::cout << std::endl;
            }
        }
    }