提升日志:使用设置文件筛选命名范围

Boost Log: Filtering named scopes using a settings file

提问人:CaTo 提问时间:11/18/2023 更新时间:11/18/2023 访问量:20

问:

我正在我的项目中使用 Boost Log,我希望能够在设置文件中筛选我当前对控制台接收器感兴趣的范围。

我使用以下方法添加了名称范围属性:

BOOST_LOG_ATTRIBUTE_KEYWORD(scope_attr, "Scope", boost::log::attributes::named_scope::value_type)

boost::log::core::get()->add_global_attribute("Scope", boost::log::attributes::named_scope());

在代码中,我使用 BOOST_LOG_NAMED_SCOPE 宏,并使用 boost::log::init_from_stream 传递以下设置:

[Core]
DisableLogging = false
Filter = "%Severity% >= DEBUG"
[Sinks.1]
Destination = Console
Format = "[%Severity%] [%TimeStamp%] [%Scope%] [%Tag%]: %Message%"
Filter = "%Scope% = \"TargetScope\" & %Severity% >= DEBUG"
[Sinks.2]
Destination = TextFile
FileName = textfile.log
AutoFlush = true
Format = "[%Severity%] [%TimeStamp%] [%Scope%] [%Tag%]: %Message%"

在未激活过滤器的情况下,“Scope”属性工作正常,并正确打印,在范围之间添加“->”,并且有些日志中只有目标范围被打印为“...[目标范围] ...”。但是,如果我激活过滤器,则不会打印任何内容。

默认情况下是否支持筛选命名范围?还是应该为过滤器解析系统编写一个扩展?

C++ 日志记录 提升 boost-log

评论


答:

1赞 sehe 11/18/2023 #1

文档

必须注意的是,默认情况下,库仅支持在库构建时已知的属性值类型。在库中注册之前,用户定义的类型将无法在解析的过滤器和格式化程序中正常工作。还可以覆盖已知类型的格式规则,包括支持字符串模板中的其他格式参数。有关此内容的更多信息,请参阅扩展库部分。

Boost 日志记录中,按命名范围过滤,您可以获得一个过滤函数(由我简化):

bool my_filter(bl::value_ref<attr::named_scope_list> const& scopes, std::string const& target_scope) {
    if (!scopes.empty())
        for (auto& scope : scopes.get())
            if (scope.scope_name == target_scope)
                return true;

    return false;
}

解析过滤器

在这里,我们必须扩展库设置支持

向筛选器分析器添加对用户定义类型的支持可提供一些步骤。

我选择了自定义(非简单)过滤器工厂,并将其与广义匹配函数相结合,如刚才所示、命名和:my_filterallany

struct scope_filter_factory : bl::filter_factory<char> {
    bl::filter on_equality_relation(bl::attribute_name const& name, string_type const& arg) override {
        return px::bind(any<std::equal_to<>>,                               //
                        expr::attr<attr::named_scope_list>(name).or_none(), //
                        arg);
    }

    bl::filter on_inequality_relation(bl::attribute_name const& name, string_type const& arg) override {
        return px::bind(all<std::not_equal_to<>>,                           //
                        expr::attr<attr::named_scope_list>(name).or_none(), //
                        arg);
    }

  private:
    using list = attr::named_scope_list;
    template <typename Rel>
    static bool any(bl::value_ref<list> const& attr, std::string const& target_scope) {
        if (attr.empty())
            return false;

        return std::any_of(begin(attr.get()), end(attr.get()), [&, cmp = Rel{}](auto const& scope) {
            return cmp(scope.scope_name, target_scope);
        });
    }
    template <typename Rel>
    static bool all(bl::value_ref<list> const& attr, std::string const& target_scope) {
        if (attr.empty())
            return true;

        return std::all_of(begin(attr.get()), end(attr.get()), [&, cmp = Rel{}](auto const& scope) {
            return cmp(scope.scope_name, target_scope);
        });
    }
};

您用 注册它,例如:register_filter_factory

void init_logging() {
    bl::core::get()->add_global_attribute("Scope", attr::named_scope());
    bl::register_filter_factory("Scope", boost::make_shared<scope_filter_factory>());
    std::ifstream ifs("logging.cfg");
    bl::init_from_stream(ifs);
}

然后,我们可以使用一些函数进行测试,例如

void bar(log_level level) {
    BOOST_LOG_NAMED_SCOPE(__FUNCTION__);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 222;
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 333;
}

void foo(log_level level) {
    BOOST_LOG_NAMED_SCOPE(__FUNCTION__);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 333;
    bar(level);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 444;
}

int main() {
    init_logging();

    for (auto level : {log_level::trace, log_level::debug, log_level::info, //
                       log_level::warning, log_level::error, log_level::fatal})
        foo(level);
}

enter image description here

完整清单

Live On Coliru(科里鲁生活公寓)

#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/phoenix.hpp>
#include <fstream>
namespace bl    = boost::log;
namespace attr  = bl::attributes;
namespace expr  = bl::expressions;
namespace px    = boost::phoenix;
using log_level = bl::trivial::severity_level;
bl::sources::severity_logger<log_level> glg;

BOOST_LOG_ATTRIBUTE_KEYWORD(severity,   "Severity", bl::trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(scope_attr, "Scope",    attr::named_scope::value_type)

// ============================================================================
// Custom filter factory
struct scope_filter_factory : bl::filter_factory<char> {
    bl::filter on_equality_relation(bl::attribute_name const& name, string_type const& arg) override {
        return px::bind(any<std::equal_to<>>,                               //
                        expr::attr<attr::named_scope_list>(name).or_none(), //
                        arg);
    }

    bl::filter on_inequality_relation(bl::attribute_name const& name, string_type const& arg) override {
        return px::bind(all<std::not_equal_to<>>,                           //
                        expr::attr<attr::named_scope_list>(name).or_none(), //
                        arg);
    }

  private:
    using list = attr::named_scope_list;
    template <typename Rel>
    static bool any(bl::value_ref<list> const& attr, std::string const& target_scope) {
        if (attr.empty())
            return false;

        return std::any_of(begin(attr.get()), end(attr.get()), [&, cmp = Rel{}](auto const& scope) {
            return cmp(scope.scope_name, target_scope);
        });
    }
    template <typename Rel>
    static bool all(bl::value_ref<list> const& attr, std::string const& target_scope) {
        if (attr.empty())
            return true;

        return std::all_of(begin(attr.get()), end(attr.get()), [&, cmp = Rel{}](auto const& scope) {
            return cmp(scope.scope_name, target_scope);
        });
    }
};

void init_logging() {
    bl::core::get()->add_global_attribute("Scope", attr::named_scope());
    bl::register_filter_factory("Scope", boost::make_shared<scope_filter_factory>());
    std::ifstream ifs("logging.cfg");
    bl::init_from_stream(ifs);
}

// ============================================================================

void bar(log_level level) {
    BOOST_LOG_NAMED_SCOPE(__FUNCTION__);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 222;
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 333;
}

void foo(log_level level) {
    BOOST_LOG_NAMED_SCOPE(__FUNCTION__);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 333;
    bar(level);
    BOOST_LOG_SEV(glg, level) << __FUNCTION__ << ":" << 444;
}

int main() {
    init_logging();

    for (auto level : {log_level::trace, log_level::debug, log_level::info, //
                       log_level::warning, log_level::error, log_level::fatal})
        foo(level);
}