首先,我想说明这个前提:
为了在日志中正确显示消息应该提供给接收器格式化程序 - 因此我需要使用不同的接收器
您不需要不同的接收器来过滤或格式化不同类型的严重性级别。您的过滤器和格式化程序必须处理这个问题,而不是接收器本身。只有在需要多个日志目标时才创建多个接收器。因此,要回答您的问题,您应该关注设置过滤器和格式化程序而不是接收器的协议。
由于您没有指定应用程序/插件系统的设计,因此很难给出确切的建议。我的意思是,必须有一些公共 API 必须由应用程序和库共享,并且您设置日志记录的方式将取决于该 API 所属的位置。除其他外,严重级别必须是该 API 的一部分。例如:
如果您正在为特定应用程序编写插件(例如媒体播放器的插件),那么应用程序就是定义插件 API 的应用程序,包括严重级别,甚至可能是插件必须使用的属性名称.应用程序使用 API 要求的属性配置接收器,包括过滤器和格式化程序,插件从不进行任何配置,只发出日志记录。请注意,API 可能包含一些允许将插件彼此区分开来的属性(例如通道名称),这将允许应用程序以不同方式处理来自不同插件的日志(例如写入不同的文件)。
如果您同时编写插件和应用程序以遵守某些可能由第三方定义的通用 API,那么日志记录协议仍必须由该 API 定义。如果不是,那么您不能假设不是您编写的任何其他应用程序或插件支持任何类型的日志记录,即使它根本使用 Boost.Log。在这种情况下,每个插件和应用程序本身都必须独立处理日志记录,这是最坏的情况,因为插件和应用程序可能会以不可预知的方式相互影响。像这样管理系统也很困难,因为每个组件都必须由用户单独配置。
如果您正在编写一个必须与多个库兼容的应用程序,每个库都有自己的 API,那么应用程序应该知道它使用的每个库中采用的日志记录约定,没有绕过它。这可能包括在库中设置回调、拦截文件输出以及在库的日志严重级别和应用程序严重级别之间进行转换。如果库使用 Boost.Log 发出日志记录,那么它们应该记录它们使用的属性,包括严重性级别,以便应用程序能够正确设置日志记录。
因此,为了采用一种方法或另一种方法,您应该首先决定您的应用程序和插件如何相互接口,它们共享哪些 API,以及该 API 如何定义日志记录。最好的情况是定义 API 时,因此您还可以设置所需的日志记录约定。在这种情况下,尽管可能,但不建议或典型地使用 API 允许的任意严重级别,因为这会使系统的实现和配置变得非常复杂。
但是,如果出于某种原因确实需要支持任意严重级别并且无法解决,您可以定义一个 API 供库提供,这可以帮助应用程序设置过滤器和格式化程序。例如,每个插件都可以提供这样的 API:
// Returns the filter that the plugin wishes to use for its records
boost::log::filter get_filter();
// The function extracts log severity from the log record
// and converts it to a string
typedef std::function<
std::string(boost::log::record_view const&)
> severity_formatter;
// Returns the severity formatter, specific for the plugin
severity_formatter get_severity_formatter();
然后应用程序可以使用一个特殊的过滤器来使用这个 API。
struct plugin_filters
{
std::shared_mutex mutex;
// Plugin-specific filters
std::vector< boost::log::filter > filters;
};
// Custom filter
bool check_plugin_filters(
boost::log::attribute_value_set const& values,
std::shared_ptr< plugin_filters > const& p)
{
// Filters can be called in parallel, we need to synchronize
std::shared_lock< std::shared_mutex > lock(p->mutex);
for (auto const& f : p->filters)
{
// Call each of the plugin's filter and pass the record
// if any of the filters passes
if (f(values))
return true;
}
// Suppress the record by default
return false;
}
std::shared_ptr< plugin_filters > pf = std::make_shared< plugin_filters >();
// Set the filter
sink->set_filter(std::bind(&check_plugin_filters, std::placeholders::_1, pf));
// Add filters from plugins
std::unique_lock< std::shared_mutex > lock(pf->mutex);
pf->filters.push_back(plugin1->get_filter());
pf->filters.push_back(plugin2->get_filter());
...
还有一个类似的格式化程序:
struct plugin_formatters
{
std::shared_mutex mutex;
// Plugin-specific severity formatters
std::vector< severity_formatter > severity_formatters;
};
// Custom severity formatter
std::string plugin_severity_formatter(
boost::log::record_view const& rec,
std::shared_ptr< plugin_formatters > const& p)
{
std::shared_lock< std::shared_mutex > lock(p->mutex);
for (auto const& f : p->severity_formatters)
{
// Call each of the plugin's formatter and return the result
// if any of the formatters is able to extract the severity
std::string str = f(rec);
if (!str.empty())
return str;
}
// By default return an empty string
return std::string();
}
std::shared_ptr< plugin_formatters > pf =
std::make_shared< plugin_formatters >();
// Set the formatter
sink->set_formatter(
boost::log::expressions::stream << "["
<< boost::phoenix::bind(&plugin_severity_formatter,
boost::log::expressions::record, pf)
<< "] " << boost::log::expressions::message);
// Add formatters from plugins
std::unique_lock< std::shared_mutex > lock(pf->mutex);
pf->severity_formatters.push_back(plugin1->get_severity_formatter());
pf->severity_formatters.push_back(plugin2->get_severity_formatter());
...
但是请注意,至少在过滤器方面,这种方法是有缺陷的,因为您允许插件定义过滤器。通常,应该由应用程序选择要记录哪些记录。为此,必须有一种方法可以将特定于库的严重性级别转换为一些常见的,可能由应用程序级别定义。