简体   繁体   English

使用 Boost.Log 的通道层次结构进行严重性和接收器过滤

[英]Use channel hiearchy of Boost.Log for severity and sink filtering

I have been studying Boost.Log for a while and I believe now is the time for me to transition my code base from log4cxx to Boost.Log.我研究 Boost.Log 有一段时间了,我相信现在是时候将我的代码库从 log4cxx 转换到 Boost.Log 了。 I believe the design and implementation of Boost.Log will significantly improve my code maintenance and usage.我相信 Boost.Log 的设计和实现将显着改善我的代码维护和使用。 I know the Boost.Log FAQ has a page that says我知道 Boost.Log FAQ 有一个页面说

As for hierarchical loggers, there is no need for this feature in the current library design.至于分层记录器,在当前的库设计中不需要此功能。 One of the main benefits it provides in log4j is determining the appenders (sinks, in terms of this library) in which a log record will end up.它在 log4j 中提供的主要好处之一是确定日志记录将在其中结束的附加程序(就此库而言,是接收器)。 This library achieves the same result by filtering.该库通过过滤实现相同的结果。

I understand the conceptual equivalence and am not trying to make Boost.Log into log4j/log4cxx.我理解概念上的等价性,并没有试图将 Boost.Log 变成 log4j/log4cxx。 Rather my question is: How do I use Boost.Log to get the same functionality that I currently use from log4cxx?相反,我的问题是:如何使用 Boost.Log 获得我目前从 log4cxx 使用的相同功能? In particular, I want to set severity thresholds and sinks for specific nodes in a log source or channel hierarchy.特别是,我想为日志源或通道层次结构中的特定节点设置严重性阈值和接收器。 For example, I have logging sources organized by libA.moduleB.componentC.logD with levels in the hierarchy separated by dots .例如,我有由libA.moduleB.componentC.logD组织的日志源,层次结构中的级别由点分隔. . . Using log4cxx one can set the overall threshold of libA to INFO with the more specific logger, libA.moduleB , having a threshold of DEBUG.使用 log4cxx 可以将libA的总体阈值设置为 INFO ,其中更具体的记录器libA.moduleB具有 DEBUG 阈值。

libA.threshold=INFO
libA.moduleB.threshold=DEBUG

Similarly one can attach sinks to arbitrary nodes in the hierarchy.类似地,可以将接收器附加到层次结构中的任意节点。

I believe that a similar capability is possible with Boost.Log but I need help/guidance on how to actually implement this.我相信 Boost.Log 可以实现类似的功能,但我需要有关如何实际实现它的帮助/指导。 Plus, I am sure others who would like to transition to Boost.Log from other frameworks will have the same question.另外,我相信其他想从其他框架过渡到 Boost.Log 的人也会有同样的问题。

I sincerely appreciate your comments.我真诚地感谢您的意见。

In Boost.Log sinks (the objects that write log files) and loggers (the objects through which your application emits log records) are not connected directly, and any sink may receive a log message from any logger.在 Boost.Log 中,sinks(写入日志文件的对象)和 loggers(应用程序通过其发出日志记录的对象)没有直接连接,任何 sink 都可能从任何 logger 接收日志消息。 In order to make records from certain loggers appear only in particular sinks you will have to arrange filters in sinks so that the unnecessary records are suppressed for sinks that are not supposed to receive them and passed for others.为了使来自某些记录器的记录仅出现在特定的接收器中,您必须在接收器中安排过滤器,以便为不应该接收它们的接收器抑制不必要的记录并为其他接收器传递。 To distinguish records from different loggers the loggers have to add distinct attributes to every record they make.为了区分来自不同记录器的记录,记录器必须为他们制作的每条记录添加不同的属性。 Typically this is achieved with channels - loggers will attach a Channel attribute that can be used to identify the logger in the filters, formatters or sinks.通常这是通过通道实现的——记录器将附加一个通道属性,该属性可用于在过滤器、格式化器或接收器中识别记录器。 Channels can be combined with other attributes, such as severity levels.通道可以与其他属性结合使用,例如严重性级别。 It must be noted though that channels and severity levels are orthogonal, and any channel may have records of any level.需要注意的是,通道和严重级别是正交的,任何通道都可能有任何级别的记录。 Values of different attributes are analyzed separately in filters.不同属性的值在过滤器中分别进行分析。

So, for example, if you want records from channel A to be written to file A.log, and from channel B - to B.log, you have to create two sinks - one for each file, and set their filters accordingly.因此,例如,如果您希望将来自通道 A 的记录写入文件 A.log,并将来自通道 B 的记录写入文件 B.log,您必须创建两个接收器 - 每个文件一个,并相应地设置它们的过滤器。

BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_channel, "Channel", std::string)

logging::add_file_log(
    keywords::file_name = "A.log",
    keywords::filter = a_channel == "A");

logging::add_file_log(
    keywords::file_name = "B.log",
    keywords::filter = a_channel == "B");

See the docs about defining attribute keywords and convenience setup functions .请参阅有关定义属性关键字便利设置函数的文档。 Now you can create loggers for each channel and log records will be routed to sinks by filters.现在您可以为每个通道创建记录器,日志记录将通过过滤器路由到接收器。

typedef src::severity_channel_logger< severity_level, std::string > logger_type;

logger_type lg_a(keywords::channel = "A");
logger_type lg_b(keywords::channel = "B");

BOOST_LOG_SEV(lg_a, info) << "Hello, A.log!";
BOOST_LOG_SEV(lg_b, info) << "Hello, B.log!";

You can have as many loggers for a single channel as you like - messages from each of them will be directed to a single sink.您可以根据需要为单个通道设置任意数量的记录器 - 来自每个通道的消息将被定向到单个接收器。

However, there are two problems here.但是,这里有两个问题。 First, the library has no knowledge of the channel nature and considers it just an opaque value.首先,图书馆不知道通道的性质,认为它只是一个不透明的值。 It has no knowledge of channel hierarchy, so "A" and "A.bb" are considered different and unrelated channels.它不知道频道层次结构,因此“A”和“A.bb”被视为不同且不相关的频道。 Second, setting up filters like above can be difficult if you want multiple channels to be written to a single file (like, "A" and "A.bb").其次,如果您希望将多个通道写入单个文件(如“A”和“A.bb”),那么像上面这样设置过滤器可能会很困难。 Things will become yet more complicated if you want different severity levels for different channels.如果您希望对不同的通道使用不同的严重级别,事情将变得更加复杂。

If channel hierarchy is not crucial for you, you can make filter configuration easier with a severity threshold filter .如果通道层次结构对您来说不重要,您可以使用严重性阈值过滤器简化过滤器配置。 With that filter you can set minimal severity level for each corresponding channel.使用该过滤器,您可以为每个相应的通道设置最低严重性级别。 If you want to inherit severity thresholds in sub-channels then your only way is to write your own filter;如果你想在子通道中继承严重性阈值,那么你唯一的方法就是编写自己的过滤器; the library does not provide that out of the box.图书馆不提供现成的。

There are multiple ways to create a filter but it boils down to writing a function that accepts attribute values from log records and returns true if this record passed the filter and false otherwise.有多种创建过滤器的方法,但归结为编写一个函数,该函数接受来自日志记录的属性值,如果该记录通过过滤器则返回true ,否则返回false Perhaps, the easiest way is shown in Tutorial , see the example with phoenix::bind from Boost.Phoenix .也许,最简单的方法显示在Tutorial中,请参阅来自Boost.Phoenixphoenix::bind示例。

bool my_filter(
    logging::value_ref< severity_level, tag::a_severity > const& level,
    logging::value_ref< std::string, tag::a_channel > const& channel,
    channel_hierarchy const& thresholds)
{
    // See if the log record has the severity level and the channel attributes
    if (!level || !channel)
       return false;

    std::string const& chan = channel.get();

    // Parse the channel string, look for it in the hierarchy
    // and find out the severity threshold for this channel
    severity_level threshold = thresholds.find(chan);

    return level.get() >= threshold;
}

Now setting up sinks would change like this to make use of your new filter:现在设置接收器会像这样改变以使用你的新过滤器:

logging::add_file_log(
    keywords::file_name = "A.log",
    keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_A));

logging::add_file_log(
    keywords::file_name = "B.log",
    keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_B));

Here hierarchy_A and hierarchy_B are your data structures used to store severity thresholds for different channels for the two log files.这里的hierarchy_Ahierarchy_B是您的数据结构,用于存储两个日志文件的不同通道的严重性阈值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM