本文首先介绍了boost.log的几个重要的概念,然后分析其框架结构,最后详细解析了一段示例代码,并总结了将boost.log应用到自己的程序中时的步骤。

1. 几个概念

  • 日志记录:一个独立的消息包,这个消息包还不是实际写到日志里的消息,它只是一个候选的消息。
  • 属性:日志记录中的一个消息片。
  • 属性值:那就是上面所说的属性的值了,可以是各种数据类型。
  • 日志槽(LOG SINK):日志写向的目标,它要定义日志被写向什么地方,以及如何写。
  • 日志源:应用程序写日志时的入口,其实质是一个logger对象的实例。
  • 日志过滤器:决定日志记录是否要被记录的一组判断。
  • 日志格式化:决定日志记录输出的实际格式。
  • 日志核心:维护者日志源、日志槽、日志过滤器等之间的关系的一个全局中的实体。主要在初始化logging library时用到。

2. 框架结构

如图,

(1). 应用程序在图的右侧,通过一个或多个logger实例发送日志消息。

(2). 应用程序也可以出现在左侧,那就是一个日志的显示实例了。

(3). 一个日志记录的数据中会包括许多属性。属性基本上是一个函数,它的返回值就是属性值。比如时间不是一个函数(也是一个属性)。

(4). 有三种类型的属性集:全局的,特定线程的,特定源的。前两个是由logging core来维护的,所以不用再初始化。

(4.1). 全局属性集中的属性被连接到所以的日志对象上。

(4.2). 线程属性集中的属性会连接到把它注册到属性集时的那个线程。

(4.3). 源属性集由初始化日志的源来维护的,它会连接到一个特定的源上。

(4.4). 当一个源初始化日志对象的时候,它会从上述的三个属性集的所有属性中得到属性值。这些值会在将来处理。

如果在不同的属性集中有相同的属性名字的时候就会造成冲突,解决冲突的方法是全局属性集的优先级最低,源属性集的优先级最高。高优先级的属性会覆盖低优先级的属性。

(5). 当组合属性值的时候,logging core来决定一个属性是否要被送到sink中,这就是过滤。有两层过滤,首先应用的是全局中过滤,全局过滤用来快速的过滤掉那些不需要的日志记录。然后就是sink指定的过滤了。每个sink都有单独的过滤器。sink过滤器允许将一个日志记录定向到一个指定的sink。

(6). 如果一个日志记录至少通过了一个sink的话,它就可以用了。这时候就是日志消息格式化的时候了。格式化完成的日志消息和属性值一起被送到接收它们的sink中。

(7). 如上图所示,sink被分为前端和后端两个部分。这是为了抽象sink的通用功能,如过滤和线程同步。前端由日志库提供,用户不大可能再去实现它。而后端很可能是在日志库的外面,它来实现对日志记录的处理。如写文件,发送到网络等。日志库提供了大部分通常用到的后端程序。

3. 示例代码解析

这个示例代码在boost.log的basic_usage里,文件前面include就省略了。

namespace logging = boost::log;
namespace fmt = boost::log::formatters;
namespace flt = boost::log::filters;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;using boost::shared_ptr;// 这里定义了一个日志级别的enum,后面在日志输出时会是一个属性值
enum severity_level
{normal,notification,warning,error,critical
};// 定义上面的级别输出流操作的重载函数,在日志输出时会用到
template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl)
{static const char* const str[] ={  // 这里的每一个值要与severity_level enum对应"normal","notification","warning","error","critical"};
    //如果日志的级别在enum里,则输出相应的文本if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str))) strm << str[lvl];else  //否则直接输出数字值strm << static_cast< int >(lvl);return strm;
}
// 这个函数用来测试日志嵌套输出的功能
int foo(src::logger& lg)
{BOOST_LOG_FUNCTION(); // 这里会在Scope属性中加入“foo”BOOST_LOG(lg) << "foo is being called";return 10;
}int main(int argc, char* argv[])
{// 创建一个sink: synchronous_sink是sink frontend, text_ostream_backend是sink backend// text_ostream_backend可以将日志以文本的方式输出// synchronous_sink可以处理线程同步的问题,也就是在多个线程同时使用这个sink时,// 我们的应用程序不用再考虑线程同步的问题了。typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;shared_ptr< text_sink > pSink(new text_sink);//好了,现在pSink就是一个text_sink类型的shared_ptr指针了{ // 这里限定的区域是为了下面的锁// 获取一个backend的锁指针.// 因为有了synchronous_sink类型的frontend,我们这里只要有这个locked_backend()// 就保证在此处操作时不会有其它的线程同时操作.text_sink::locked_backend_ptr pBackend = pSink->locked_backend();// 既然backend是一个text_ostream类型的,我们就可以加入一些ostream类型的输出流给他// 日志会同时输出到这些输出流中// 先加一个std::clog给它shared_ptr< std::ostream > pStream(&std::clog, logging::empty_deleter());// shared_ptr会在指针不再使用时删除它, 但std::clog是不能删除的, 所以加logging::empty_deleter()pBackend->add_stream(pStream);// 再加一个std::ofstream给它shared_ptr< std::ofstream > pStream2(new std::ofstream("sample.log"));assert(pStream2->is_open());pBackend->add_stream(pStream2);}// 好了,我们已经做好了一个sink, 现在将它加入到logging library里logging::core::get()->add_sink(pSink);// 再创建一个logger, 我们就可以用它来输出了.src::logger lg;// Hello, World 一下, 在sample.log文件和控制台上会同时显示BOOST_LOG(lg) << "Hello, World!";// 格式化输出, 也是用locked_backend来操作, 此时指定的属性要在后台逐一定义.pSink->locked_backend()->set_formatter(fmt::ostrm<< fmt::attr("LineID") // 这个是指日志文件的行号,不是程序源文件的行号<< " [" << fmt::date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f") << "] [" << fmt::attr< severity_level >("Severity") // 注意这里的severity_level正是我们前面定义的enum<< "] [" << fmt::time_duration< boost::posix_time::time_duration >("Uptime") // 这个属性在后面会被定义成一个线程范围的属性<< "] [" << fmt::attr< std::string >("Tag") // 这个Tag只是一个字符串类型的属性<< "] [" << fmt::named_scope("Scope", fmt::keywords::scope_iteration = fmt::keywords::reverse) << "] " // 这个Scope属性就是打印嵌套函数的东西了<< fmt::message()); // 最后,别忘了将最重要的日志内容打印了./*
    // 这是另外一种格式化的方法, 好像更简单一些.
    pSink->locked_backend()->set_formatter(
        fmt::format("%1% @ %2% [%3%] >%4%< Scope: %5%: %6%")
            % fmt::attr("LineID")
            % fmt::date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f")
            % fmt::time_duration< boost::posix_time::time_duration >("Uptime")
            % fmt::attr< std::string >("Tag")
            % fmt::named_scope("Scope", fmt::keywords::scope_iteration = fmt::keywords::reverse, fmt::keywords::scope_depth = 2)
            % fmt::message());
*/// 下面开始设置属性了.// LineID是一个计数器,先创建一个初始值为1的计数器.shared_ptr< logging::attribute > pCounter(new attrs::counter< unsigned int >(1));// 将它加入到全局属性中,如果要求将不同的内容输出到不同的日志文件中去,这里设置为全局属性可能就是不太合适了.logging::core::get()->add_global_attribute("LineID", pCounter);// 下面是设置TimeStamp属性shared_ptr< logging::attribute > pTimeStamp(new attrs::local_clock());logging::core::get()->add_global_attribute("TimeStamp", pTimeStamp);// 设置Uptime属性为线程级属性,因为运行时间只能在一个线程内衡量才有意义// attrs::timer应该是一个boost::posix_time::time_duration类型的值,会记录上本次调用与上一次调用的时间差。BOOST_LOG_SCOPED_THREAD_ATTR("Uptime", attrs::timer);// Socpe也是一个线程级的属性,add_thread_attribute是另外一个增加线程级属性的方法boost::shared_ptr< logging::attribute > pNamedScope(new attrs::named_scope());logging::core::get()->add_thread_attribute("Scope", pNamedScope);// 设置日志的Scope,也就是“main”函数BOOST_LOG_FUNCTION();// 现在再输出两个日志记录,结果是这样的:// 1 [08.12.2009 11:16:42.750000] [] [00:00:00.000079] [] [int __cdecl main(int,char *[])] Some log line with a counter// 2 [08.12.2009 11:16:42.765625] [] [00:00:00.016310] [] [int __cdecl main(int,char *[])] Another log line with the counterBOOST_LOG(lg) << "Some log line with a counter";BOOST_LOG(lg) << "Another log line with the counter";// 注意到上面有两个空的属性,一个是severity_leve, 另一个是Tag// 下面设置一下Tag.{BOOST_LOG_NAMED_SCOPE("Tagging scope"); // 这里设定一个这个区域的名字为“Tagging scope”,输出scope属性值时会增加这个scope// 现在增加给lg增加一个临时的属性.// 每一个在当前scope里用lg输出的日志记录,它的“Tag”属性值都是“Tagged line”BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Tag", std::string, "Tagged line");// 也可以用下面的代码实现:// attrs::constant< std::string > TagAttr("Tagged line");// logging::scoped_attribute _ =//     logging::add_scoped_logger_attribute(lg, "Tag", TagAttr);// 再输出两条看一下,结果是这样的, 注意“Tagged line”和“Tagging scope”:// 3 [08.12.2009 11:16:42.781250] [] [00:00:00.032886] [Tagged line] [Tagging scope<-int __cdecl main(int,char *[])] Some tagged log line// 4 [08.12.2009 11:16:42.812500] [] [00:00:00.051012] [Tagged line] [Tagging scope<-int __cdecl main(int,char *[])] Another tagged log lineBOOST_LOG(lg) << "Some tagged log line";BOOST_LOG(lg) << "Another tagged log line";}// 这里再输出一行,就没有上面那个区域中的“Tag line”和“Tagging scope”了:// 5 [08.12.2009 11:16:42.828125] [] [00:00:00.068013] [] [int __cdecl main(int,char *[])] Now the tag is removedBOOST_LOG(lg) << "Now the tag is removed";// 现在可以看一下过滤器的使用了。// 过滤器的过滤条件是基于属性的。// 每一个过滤器其实就是一个返回值为bool型的函数对象.// 过滤器可以指定到sink,也可以指定到全局.// 像下面这样可以为一个sink设置过滤器://pSink->set_filter(//    flt::attr< severity_level >("Severity") >= warning // 输出所有Severity属性值大于等于warning的日志记录//    || flt::attr< std::string >("Tag").begins_with("IMPORTANT")); // 或者Tag属性值以“IMPORTANT”开头的// 对于std::string或std::wstring类型的属性有一些谓词可以使用:// "begins_with", "ends_with", "contains", "matches"// 其中matches谓词可以RegEx表达式// 下面是设置全局的过滤器logging::core::get()->set_filter(flt::attr< severity_level >("Severity") >= warning // Write all records with "warning" severity or higher|| flt::attr< std::string >("Tag").begins_with("IMPORTANT"));// 这时候,我们可以用“lg”来输出日志记录了。// // 另外,还有一个severity_logger,可以直接使用它来做logger// 如果想增加一些功能,可以派生于它的类src::severity_logger< severity_level > slg;// 由于我们前面设置了过滤器(不论是全局的还是sink的都影响),所以下一行的normal日志记录将不会输出。其它是设置为全局和线程的属性对于slg也同样适用。BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the output";BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the output";{// 这里设置一个以“IMPORTANT”开头的Tag属性BOOST_LOG_SCOPED_THREAD_TAG("Tag", std::string, "IMPORTANT MESSAGES");// 下面再用slg输出一个normal日志。// 这里没有指定level,但severity_logger默认级别为0,在这个程序里就是normal// 也可以指定severity_logger的默认级别BOOST_LOG(slg) << "Some really urgent line";}
    // reset_filter()了sink的filter,如果前面设置了sink的过滤器,这里会取消掉。但全局的不会被resetpSink->reset_filter();// 下面会先输出foo里的日志记录,然后再输出这个日志记录BOOST_LOG(lg) << "The result of foo is " << foo(lg);return 0;
}

4. 总结

boost.log框架主要是由日志源,全局库,sink组成。

  • 在上面的程序中,日志源就是src::logger lg和src::severity_logger< severity_level > slg。
  • 全局库就是logging::core::get()。
  • sink就是pSink,这是一个sinks::synchronous_sink< sinks::text_ostream_backend >类型的指针。

sink的backend可以设置输出流,可以设置输出格式。

属性可以设置为全局的,线程的,日志源的。也可以在一个区域中设置一个临时的属性。

问: 说,要将日志放文件里, 总共分几步?

  • 答: 总共分三步
  • 第一步、 创建一个sink, 向sink加入文件输出流;
  • 第二步、将sink加入到logging library里, 并创建一个logger;
  • 第三步、向logger输出日志记录。

5. 其它

  • 如何限制日志文件的长度?
// 创建一个格式化对象, 另一种格式化的方法
boost::function< void (std::ostream&, logging::attribute_values_view const&, std::string const&) > formatter =fmt::ostrm<< fmt::attr< unsigned int >("LineID", "[%09u] ")<< fmt::date_time< boost::posix_time::ptime >("TimeStamp") << " *"<< fmt::message();// 创建一个sink
boost::shared_ptr< sinks::synchronous_sink< sinks::text_ostream_backend > > sink(new sinks::synchronous_sink< sinks::text_ostream_backend >);// 增加常用属性,也就是LineID和TimeStamp这两个属性。这个方便一点
logging::add_common_attributes();// 设置sink的输出格式
sink->locked_backend()->set_formatter(formatter);// 日志文件将1个小时换一个,且文件大小不会超过1MB。文件名从file_00.log开始
sink->locked_backend()->add_stream(boost::make_shared< boost::log::rotating_ofstream >("file_%02N.log", logging::keywords::rotation_interval = 3600, logging::keywords::rotation_size = 1048576));// 将sink加到logging::core里面
logging::core::get()->add_sink(sink);

Boost log库相关推荐

  1. boost::log模块实现从设置文件初始化库的示例

    boost::log模块实现从设置文件初始化库的示例 实现功能 C++实现代码 实现功能 boost::log模块实现从设置文件初始化库的示例 C++实现代码 #include <excepti ...

  2. boost::log模块实现从设置文件初始化库的示例,具有自定义过滤器和格式化程序工厂的属性

    boost::log模块实现从设置文件初始化库的示例,具有自定义过滤器和格式化程序工厂的属性 实现功能 C++实现代码 实现功能 boost::log模块实现从设置文件初始化库的示例,具有自定义过滤器 ...

  3. boost使用log库编译报错

    2019独角兽企业重金招聘Python工程师标准>>> 使用boost的log,用以下链接选项: -lboost_system -lboost_thread -lboost_log ...

  4. Boost Log : Setting up sinks

    Setting up sinks 有时候,trivial(简单的)logging并不能满足要求.例如,想要更精细的日志处理,而不是简单地打印出来.为此,必须构建自定义的sinks,并且将它们注册到co ...

  5. 使用Boost::Log记录日志

    使用Boost的Log库,将日志信息写入文件,日志将按时间滚动.详见代码: #include <boost/date_time/posix_time/posix_time_types.hpp&g ...

  6. 动态库链接boost静态库

    为了避免项目布署麻烦,需要将执行文件尽量静态链接 1. boost库全部静态链接 2. c++库静态链接 1,2点的改变如下,强制链静态库的方法为参数下为-l:libXXXX.a; 对于boost l ...

  7. boost log简介

    文章目录 前言 编译过程 一步步介绍boost-log boost-log的hello world boost-log的结构 boost-log的简单使用 按需定制日志库 - 略 前言 boost日志 ...

  8. logger(三):其他log库的介绍(glog的用法)

    Glog等一些log库 在写代码的过程中,打log肯定是少不了的,毕竟不能总靠调试来发现问题.我们可以试着使用自己写一些log库,也可以使用现成的log库,成熟的log库非常多,log4cpp.log ...

  9. Boost C++ 库

    http://zh.highscore.de/cpp/boost/frontpage.html Boost C++ 库 目录 第 1 章 简介 第 2 章 智能指针 第 3 章 函数对象 第 4 章  ...

最新文章

  1. VSCode搭建Vue项目
  2. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
  3. 转:c/c++ 运行库
  4. 同一个页面生成多个sessionid_web页面渲染(一)
  5. PHPUnit 3.4.10 在windows上配置
  6. 编写下载服务器。 第三部分:标头:内容长度和范围
  7. JZ2440用U-Boot给Nand-Flash烧写程序时报错:NAND write: incorrect device type in bootloader ‘bootloader‘ is not
  8. [Leedcode][JAVA][面试题 16.03. 交点]
  9. ppt给图片增加高斯模糊_【毕业答辩】PPT美化:如何设计毕业答辩的封面
  10. Spark Streaming实例
  11. 【深度学习】全面理解VGG16模型
  12. IPtable 工作原理
  13. python产品管理系统_python实现超市商品销售管理系统
  14. 陈如波律师:孙宇晨说自己“合法合规”站得住脚吗?
  15. 百练:4151 电影节
  16. html标签 lt heavy gt,HTML Purifier:转换&lt; body&gt;到&lt; div&gt;
  17. Word2010如何隐藏去掉回车符
  18. 黑盒测试之导入CVS文件之什么是CVS文件
  19. php pdf转txt文件,PDF文件在线转换TXT
  20. 电大计算机画图程序属性,电大计算机绘图(本)复习大全.doc

热门文章

  1. css禅意花园讲了什么——读书笔记1
  2. Java log4j详细教程
  3. 架构师速成5.2-价值观和目标
  4. 阿里云注销备案流程及注销备案常见问题与解答
  5. 微信怎么查计算机成绩查询,如何用微信免费查询自己的成绩?——易查分快速帮您实现...
  6. nginx下强制跳转到www域名,域名重定向
  7. java web 开发——第一章jsp简介
  8. 动手深度学习13:计算机视觉——语义分割、风格迁移
  9. 六张图,看懂前瞻性数据分析,该如何做
  10. 看完知乎轮子哥的编程之路,我只想说,收下我的膝盖...