boost.log(五)属性

在前面几节中我们提到属性和属性值好几次。在这节我们会学习如何使用属性,以添加更多的数据到日志记录。

每条日志记录可以附加多个已命名的属性值,属性可以代表日志记录产生时任何与程序运行相关的数据信息。如代码位置、执行模块名称、当前数据和时间以及程序运行相关的任何数据信息。属性可以表现为一个值生成器,在这种情况下,将用于返回它参与的每个不同日志记录所生成的值。一旦属性生成值,就可以把这个值用于过滤器,格式化器和接收器。但是使用该属性值你必须知道它的名称和类型。boost.log库中实现了常用的属性,你可以在文档中找到它们值的类型。

除此之外,属性有三种可能的范围:局部范围,特定线程和全局。当进行日志记录,这三个范围的属性值被加入到一个集合并传递到接收器。这意味着属性的来源在接收器中没有区别。任何属性可以在任何范围内进行注册。当注册时属性会被赋予一个唯一的名称,使得能够搜索它。如果碰巧几个范围中有同样名称的属性,那么局部范围的属性将被优先考虑并做出进一步的处理,包括日志过滤和输出格式化。这种行为能够覆盖全局和线程范围的属性在你本地记录器中的注册,从而减少线程干扰。

下面是属性注册过程的描述。

常用属性

几乎任何程序都有可能被用到的属性,如计数器、时间戳等,它们可以通过调用一个函数很容易地被添加:

  1. #include <boost/log/utility/setup/common_attributes.hpp>
  2. int main()
  3. {
  4. boost::log::add_common_attributes();
  5. return 0;
  6. }

调用add_common_attributes这个函数将在全局注册"LineID"、"TimeStamp"、"ProcessID"和"ThreadID"。"LineID"属性是一个计数器,每个纪录递增。"TimeStamp"属性总是会产生当前时间 。最后两个记录日志产生时当前进程和线程的ID。

[注意]在单线程情况下"ThreadID"属性不会被注册。

[提示]默认情况下,当应用程序启动时,log库中没有注册任何属性。应用程序必须在开始写日志之前在log库中注册所有必要的属性。这作为log库初始化的一部分。但是为什么第一节中BOOST_LOG_TRIVIAL宏可以正常工作呢?答案是默认接收器除了严重级别没有使用任何属性值来组成它的输出。这样做是为了避免需要Trivial Logging进行初始化。如果你一旦使用过滤器或格式化或者非默认接收器,那么你必须注册你所需要的属性。


某些属性可以在记录器构造时自动注册。例如,severity_logger 注册特定源属性可以用来添加强调不同的日志记录级别的"Severity"。举个例子:

  1. #include <boost/log/sources/record_ostream.hpp>
  2. #include <boost/log/sources/severity_logger.hpp>
  3. #include <boost/log/sources/severity_feature.hpp>
  4. #include <boost/log/utility/setup/common_attributes.hpp>
  5. enum severity_level
  6. {
  7. normal,
  8. notification,
  9. warning,
  10. error,
  11. critical
  12. };
  13. void logging_function()
  14. {
  15. boost::log::sources::severity_logger< severity_level > slg;
  16. BOOST_LOG_SEV(slg, normal) << "A regular message";
  17. BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
  18. BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
  19. }

BOOST_LOG_SEV 宏的行为很像 BOOST_LOG, 不同在于它需要为open_record函数传入一个参数,BOOST_LOG_SEV  宏与下面的代码等价:

  1. #include <boost/log/keywords/severity.hpp>
  2. #include <boost/log/sources/record_ostream.hpp>
  3. #include <boost/log/sources/severity_logger.hpp>
  4. #include <boost/log/sources/severity_feature.hpp>
  5. #include <boost/log/utility/setup/common_attributes.hpp>
  6. enum severity_level
  7. {
  8. normal,
  9. notification,
  10. warning,
  11. error,
  12. critical
  13. };
  14. void manual_logging()
  15. {
  16. boost::log::sources::severity_logger< severity_level > slg;
  17. boost::log::record rec = slg.open_record(boost::log::keywords::severity = normal);
  18. if (rec)
  19. {
  20. boost::log::record_ostream strm(rec);
  21. strm << "A regular message";
  22. strm.flush();
  23. slg.push_record(boost::move(rec));
  24. }
  25. }

在这里你可以看到 open_record 可以使用命名的参数。由boost.log库提供的一些记录器类型都支持这种额外的参数,这种做法也可以在用户编写自己的记录器时使用。

更多属性

现在我们看看在调用add_common_attributes函数时,它具体做了什么。

  1. inline void add_common_attributes()
  2. {
  3. shared_ptr< core > pCore = core::get();
  4. pCore->add_global_attribute(
  5. aux::default_attribute_names::line_id(),
  6. attributes::counter< unsigned int >(1));
  7. pCore->add_global_attribute(
  8. aux::default_attribute_names::timestamp(),
  9. attributes::local_clock());
  10. pCore->add_global_attribute(
  11. aux::default_attribute_names::process_id(),
  12. attributes::current_process_id());
  13. #if !defined(BOOST_LOG_NO_THREADS)
  14. pCore->add_global_attribute(
  15. aux::default_attribute_names::thread_id(),
  16. attributes::current_thread_id());
  17. #endif
  18. }

这里的line_id、timestamp等类型,它们由共同的接口父类attribute派生。log库还提供了很多其它的属性类(参见log\detail\default_attribute_names.hpp)。包含函数对象属性,通过调用一些函数来获取值。例如,我们以类似的方式注册一个名字为 named_scope 的属性:

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

这将使应用程序我们能够将范围名称存储在每个日志记录中。下面是如何使用它:

  1. void named_scope_logging()
  2. {
  3. BOOST_LOG_NAMED_SCOPE("named_scope_logging");
  4. boost::log::sources::severity_logger< severity_level > slg;
  5. BOOST_LOG_SEV(slg, normal) << "Hello from the function named_scope_logging!";
  6. }

记录器特有的属性并不比那些全局的属性有用。严重级别和渠道名是程序执行中最明显的属性。没有什么能阻止你为你的记录器增加更多的属性,像这样:

  1. void tagged_logging()
  2. {
  3. boost::log::sources::severity_logger< severity_level > slg;
  4. slg.add_attribute("Tag", boost::log::attributes::constant< std::string >("My tag value"));
  5. BOOST_LOG_SEV(slg, normal) << "Here goes the tagged record";
  6. }

现在通过此日志记录器的所有日志记录都将被标记“Tag”这个特定的属性。此属性的值可用于过滤筛选或者是自定义输出格式。

属性的另一个好用途是在应用程序执行一个过程中,日志记录有标记此过程中不同部分的能力。我们甚至可以实现一个粗略的分析工具来检测性能瓶颈。举个例子:

  1. void timed_logging()
  2. {
  3. BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", boost::log::attributes::timer());
  4. boost::log::sources::severity_logger< severity_level > slg;
  5. BOOST_LOG_SEV(slg, normal) << "Starting to time nested functions";
  6. logging_function();
  7. BOOST_LOG_SEV(slg, normal) << "Stopping to time nested functions";
  8. }

现在每个日志记录从 logging_function 函数中取得,或调用任何其他函数获得。记录中将包含具有一种"Timeline"属性的高精度时间(通过BOOST_LOG_SCOPED_THREAD_ATTR 宏注册的属性)。根据这些时间,我们可以很容易检测到哪些代码耗费了更多的时间来执行。由于BOOST_LOG_SCOPED_THREAD_ATTR 注册的属性是范围内属性,所有离开timed_logging函数的作用域这个属性就会被注销。

定义属性占位符

我们将在接下来的几节中看到这种方式:定义关键字来描述特定属性。此关键字能够参与到日志过滤和输出格式的设定中,像前面各节中的严重级别占位符。例如,要声明上面例子中的那些属性,可以这样定义占位符:

  1. BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
  2. BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
  3. BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
  4. BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", boost::log::attributes::named_scope::value_type)
  5. BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", boost::log::attributes::timer::value_type)

每个宏定义一个关键字。第一个参数是占位符的名字,第二个是属性名,最后一个参数是属性值类型。一旦定义,占位符可以在模板表达式和log库的一些其它环境中使用。上面定义的属性关键字的更多细节可以在这里找到。

[注意]定义属性占位符并不会去注册属性!

时间: 2024-08-29 12:16:56

boost.log(五)属性的相关文章

boost.log(九) 配置文件

前面几节中描述了Boost.Log 的基础知识,对Boost.Log 库的操作我们都是在C++代码中进行中,这样就会有一些不便的地方.比如说我们想要更改一下输出格式或者过滤条件,都必须对C++代码进行更改,并且还得编译一次(感觉编译时间还有点长).其实Boost.Log 里面已经为这个问题提供了一种解决方案,就是通过配置文件来初始化Boost.Log 库,C++这边的代码也比较简单,就是这样的: std::ifstream settings("settings.txt"); if (!

boost.log要点笔记

常用简写: namespace logging = boost::log; namespace src = boost::log::sources; namespace expr = boost::log::expressions; namespace sinks = boost::log::sinks; namespace attrs = boost::log::attributes; namespace keywords = boost::log::keywords; 要点: 结构图要牢记在

Boost Log 基本用法

Boost Log 基本用法 flyfish 2014-11-5 根据boost提供的代码示例,学习Boost Log 的基本用法 前提 boost版本boost_1_56_0 示例代码文件夹 boost_1_56_0\libs\log\example\basic_usage 使用的单词很形象,整个过程就像流水一样 假设要输出的日志比作水 水                     (Hello, World!) 水槽                 (sink) 流向哪里        (co

boost.log(八)宽字符记录

宽字符记录 Boost.Log支持包含本地字符集字符串的日志记录.基本上有两种方式做这件事.在 UNIX 系统上通常使用一些多字节字符编码 (例如 UTF-8) 用来表示本地字符.在这种情况下,Boost.Log库可以直接以纯 ASCII 的方式记录而不需要其它额外的设置. 在Windows 上常见的做法是使用宽字符串来表示本地字符串.此外大多数系统 API 也是使用的宽字符,这需要特定于 Windows 的接收器也支持宽字符.另一方面,通用的接收器,例如 TextFile,是面向字节的,你写入

boost.log(七)再谈过滤

再谈过滤 我们已经在前面的章节接触过滤,但只是浅尝辄止.我们现在能够添加日志记录并设置接收器的属性,我们需要建立复杂的过滤功能.让我们看下这个例子: #include <string> #include <fstream> #include <iomanip> #include <boost/log/core.hpp> #include <boost/smart_ptr.hpp> #include <boost/log/sinks.hpp&

boost.log(六)格式化

如果你尝试运行上一节中的例子,你会发现只有日志记录消息(没有时间戳等属性信息)会被写入到文件.这是因为boost.log库没有设置格式化.即使你添加了属性到boost.log的core或者是记录器中,记录值也不会被输出,除非你指定了这些值的格式. 回到在前面节教程的一个例子: #include <iostream> #include <boost/log/utility/setup/file.hpp> #include <boost/log/sinks/basic_sink_

boost.log(二)过滤

日志过滤 严重级别可以使日志信息更加翔实,但通常也会成为筛选器用来过滤日志记录的工具.在Log库的core里面可以通过设置全局过滤器很容易地做到日志过滤,就像下面一样: #include <iostream> #include <boost/log/core.hpp> #include <boost/log/trivial.hpp> #include <boost/log/expressions.hpp> void init() { boost::log::

Boost Log

boost log支持以下配置宏,只列出一些常用的,如下表所示: Macro name Effect BOOST_LOG_DYN_LINK If defined in user code, the library will assume the binary is built as a dynamically loaded library (“dll” or “so”). Otherwise it is assumed that the library is built in static mo

Boost Log : Attributes

Adding more information to log: Attributes 在前面的章节中,我们多次提到了属性和属性值.在这里,我们将发现如何使用属性向日志记录添加更多的数据. 每一个日志记录都可以包含许多命名的属性值.属性可以表示任何关于日志记录发生的条件的基本信息,比如代码中的位置,可执行的模块名,当前日期和时间,或者任何与您的特定应用程序和执行环境相关的数据.一个属性可以表现为一个值生成器,在这种情况下,它会为它所涉及的每个日志记录返回一个不同的值.只要属性生成该值,后者就会独立