现在的位置: 首页 > 综合 > 正文

boost log库使用 十二 架构研究和主要使用总结

2014年03月31日 ⁄ 综合 ⁄ 共 7907字 ⁄ 字号 评论关闭
文章目录

前面做了不少boost log的例子,现在来总结一下,最新代码在我的开源项目:https://gitlab.com/mystudy/boost_log

下面是boost log库的架构图:

 下面总结一下和这个架构相关的知识:

1. 如何获得Logging core

#include <boost/log/core.hpp>
...
boost::shared_ptr<logging::core> core = logging::core::get();

2. 如何安装Sink对象

一个core可以安装多个Sink,下面的代码假定已经有了两个Sink对象,将其安装到core中

  core->add_sink(sink1);
...
  core->add_sink(sink2);

3. 如何创建一个Sink对象

需要先创建一个backend对象,然后在创建sink对象的时候,将backend对象传递给它。

  typedef sinks::synchronous_sink<sinks::text_file_backend> TextSink;
  // init sink1
  boost::shared_ptr<sinks::text_file_backend> backend1 = boost::make_shared<sinks::text_file_backend>(
		  keywords::file_name = "sign_%Y-%m-%d_%H-%M-%S.%N.log",
		  keywords::rotation_size = 10 * 1024 * 1024,
		  keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
		  keywords::min_free_space = 30 * 1024 * 1024);
  boost::shared_ptr<TextSink> sink1(new TextSink(backend1));

4. 如何创建一个backend对象

指定frontend类型

前面的代码中已经演示,注意backend的类型需要制定一个frontend类型作为其模板类。因此,当创建一个backend对象的时候,已经确定了frontend。

这个frontend模板可以用synchronous_sink类,也可以用asynchronous_sink, 后者不会阻塞调用程序,会额外的创建线程去处理log,不过会慢点,内存消耗大点。一般都推荐先用后者。

用keywords构造参数

这里看到一个概念:keywords. 在boost/log/keywords/目录下27个hpp文件:

auto_flush.hpp  facility.hpp   ident.hpp       log_source.hpp      open_mode.hpp        rotation_size.hpp  target.hpp
channel.hpp     file_name.hpp  ip_version.hpp  max_size.hpp        order.hpp            scan_method.hpp    time_based_rotation.hpp
delimiter.hpp   filter.hpp     iteration.hpp   message_file.hpp    ordering_window.hpp  severity.hpp       use_impl.hpp
depth.hpp       format.hpp     log_name.hpp    min_free_space.hpp  registration.hpp     start_thread.hpp

keywords是boost库的基本概念,设计到一个宏BOOST_PARAMETER_KEYWORD,定义在boost/parameter/keywords.hpp文件中, 主要作用就是在指定的namespace中创建一个singleton的对象。所以上面的几行keywords:: 代码就是给keywords namespace下面的几个singleton对象file_name, rotation, time_based_rotation和min_free_space赋值。关键是要看看下面这个类的构造函数如何使用这些keywords.

sinks::text_file_backend

参考文档:

http://boost-log.sourceforge.net/libs/log/doc/html/log/detailed/sink_backends.html

http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/boost/log/sinks/text_file_backend.html

注意,text_file_backend的构造函数语法上支持变参,但是语义上只支持有限的keywords:

    template< typename ArgsT >
    void construct(ArgsT const& args)
    {
        construct(
            filesystem::path(args[keywords::file_name | filesystem::path()]),
            args[keywords::open_mode | (std::ios_base::trunc | std::ios_base::out)],
            args[keywords::rotation_size | (std::numeric_limits< uintmax_t >::max)()],
            args[keywords::time_based_rotation | time_based_rotation_predicate()],
            args[keywords::auto_flush | false]);
    }

文档中也的确如此描述。但是在text_file_backend.hpp文件中发现还是有关于min_free_space的代码:

namespace aux {

    //! Creates and returns a file collector with the specified parameters
    BOOST_LOG_API shared_ptr< collector > make_collector(
        filesystem::path const& target_dir,
        uintmax_t max_size,
        uintmax_t min_free_space
    );
    template< typename ArgsT >
    inline shared_ptr< collector > make_collector(ArgsT const& args)
    {
        return aux::make_collector(
            filesystem::path(args[keywords::target]),
            args[keywords::max_size | (std::numeric_limits< uintmax_t >::max)()],
            args[keywords::min_free_space | static_cast< uintmax_t >(0)]);
    }

} // namespace aux

所以估计还是可以使用target, max_size 和 min_free_space这些keywords. 以后试了就知道了。

5. 如何在sink中指定格式

下面到了指定日志格式,这个需要在sink中指定,比如:

  sink1->set_formatter (
			expr::format("[%1%]<%2%>(%3%): %4%")
			% expr::format_date_time< boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S")
			% expr::attr<sign_severity_level>("Severity")
			% expr::attr<attrs::current_thread_id::value_type>("ThreadID")
			% expr::smessage
			);

Boost::Format风格

这里的关键是理解expr::format. 文档在这里:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/detailed/expressions.html#log.detailed.expressions.formatters

我使用的是Boost::Format风格。

attributes

参考文档:http://boost-log.sourceforge.net/libs/log/doc/html/log/detailed/attributes.html

根据设计,日志记录是由attributes组成的,所以打印内容必须以attribute的方式传给sink对象。

  sink1->set_formatter (
			expr::format("[%1%]<%2%>(%3%)(%4%): %5%")
			% expr::attr<unsigned int>("LineID")
			% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
			% expr::attr<sign_severity_level>("Severity")
			% expr::attr<attrs::current_thread_id::value_type >("ThreadID")
			% expr::smessage
			);

不要忘记添加commont attributes

  logging::add_common_attributes();

该函数定义在boost/log/utility/setup/common_attributes.hpp文件中, 里面添加了4个属性:

inline void add_common_attributes()
{
    shared_ptr< core > pCore = core::get();
    pCore->add_global_attribute(
        aux::default_attribute_names::line_id(),
        attributes::counter< unsigned int >(1));
    pCore->add_global_attribute(
        aux::default_attribute_names::timestamp(),
        attributes::local_clock());
    pCore->add_global_attribute(
        aux::default_attribute_names::process_id(),
        attributes::current_process_id());
#if !defined(BOOST_LOG_NO_THREADS)
    pCore->add_global_attribute(
        aux::default_attribute_names::thread_id(),
        attributes::current_thread_id());
#endif
}

6. Name scope

Name scope也是前面格式的一种解决方案,但是由于它比较复杂,所以单独描述。

stack element

首先了解一下结构体named_scope_entry, 文档在:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/boost/log/attributes/named_scope_entry.html

named_scope_entry包含了scope_name, file_name和 line(源代码行号), 每个stack element 就是一个name_scope_entry对象。

scope stack

scope有很多种,具体参考文档:http://en.wikipedia.org/wiki/Scope_(computer_science)

最常见的是函数,此时scope stack就是函数栈。

boost log可以打印scope stack的信息到日志中

named_scope属性

named scope属性可以添加到全局属性中,这是线程相关的。添加属性代码为:

logging::core::get()->add_global_attribute("Scope", attrs::named_scope());

参考文档:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/boost/log/attributes/named_scope.html

设置格式

文档:http://www.boost.org/doc/libs/1_54_0/libs/log/doc/html/log/detailed/expressions.html#log.detailed.expressions.formatters.named_scope

下面是个简单的例子:

首先设置格式的时候添加一种格式:

			% expr::format_named_scope("Scopes", boost::log::keywords::format = "%n (%f : %l)")

然后添加属性:

  core->add_global_attribute("Scopes", attrs::named_scope());

之后调用代码中添加一个Bar和Foo函数,此处参考官方文档:http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/detailed/attributes.html#log.detailed.attributes.named_scope

void Bar() {
  int x = 0;
}


void Foo(int n) {
  src::severity_logger_mt<sign_severity_level>& lg = my_logger::get();
  // Mark the scope of the function foo
  BOOST_LOG_FUNCTION();

  switch (n)
    {
    case 0:
      {
	// Mark the current scope
	BOOST_LOG_NAMED_SCOPE("case 0");
	BOOST_LOG(lg) << "Some log record";
	Bar(); // call some function
      }
      break;

    case 1:
      {
	// Mark the current scope
	BOOST_LOG_NAMED_SCOPE("case 1");
	BOOST_LOG(lg) << "Some log record";
	Bar(); // call some function
      }
      break;

    default:
      {
	// Mark the current scope
	BOOST_LOG_NAMED_SCOPE("default");
	BOOST_LOG(lg) << "Some log record";
	Bar(); // call some function
      }
      break;
    }
}

最后在main.cc函数中调用:

  Foo(1);

执行结果:

[8]<2014-03-01 23:49:19>(0)(0x00007f21bf00e740)(void Foo(int) (./main.cc : 11)->case 1 (./main.cc : 27)): Some log record

注意,上面的代码中用到两个宏:BOOST_LOG_NAMED_SCOPE和BOOST_LOG_FUNCTION。其实就是一个宏,后面的宏只是前面宏的简化版本,可以自动将当前函数作为scope name。定义在/boost/log/attributes/named_scope.hpp文件

/*!
 * Macro for function scope markup. The scope name is constructed with help of compiler and contains current function name.
 * The scope name is pushed to the end of the current thread scope list.
 *
 * Not all compilers have support for this macro. The exact form of the scope name may vary from one compiler to another.
 */
#define BOOST_LOG_FUNCTION() BOOST_LOG_NAMED_SCOPE(BOOST_CURRENT_FUNCTION)

而BOOST_LOG_NAMED_SCOPE宏也在这个文件中定义:

/*!
 * Macro for scope markup. The specified scope name is pushed to the end of the current thread scope list.
 */
#define BOOST_LOG_NAMED_SCOPE(name)\
    BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), name, __FILE__, __LINE__)

#define BOOST_LOG_NAMED_SCOPE_INTERNAL(var, name, file, line)\
    BOOST_LOG_UNUSED_VARIABLE(::boost::log::attributes::named_scope::sentry, var, (name, file, line));

文件/boost/log/utility/unused_variable.hpp中用了GCC编译器指令__attribute__

#if defined(__GNUC__)

//! The macro suppresses compiler warnings for \c var being unused
#define BOOST_LOG_UNUSED_VARIABLE(type, var, initializer) __attribute__((unused)) type var initializer

#else

所以在FOO函数中使用BOOST_LOG_FUNCTION的时候,也就是写下了这行代码:

__attribute__((unused)) ::boost::log::attributes::named_scope::sentry _boost_log_named_scope_sentry_18 (__PRETTY_FUNCTION__, "./main.cc", 18);;

所以可以看到,这里的代码行数是固定的18, 因此如果在记录日志时想要显示代码的行数,写入日志时必须至少两行宏:

BOOST_LOG_FUNCTION 和 BOOST_LOG_SEV宏。

7. filter

filter可以过滤日志级别,还可以更多,下面是个例子:

  sink1->set_filter(expr::attr<sign_severity_level>("Severity") >= trace);

高级filter的文档在这里:

http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/tutorial/advanced_filtering.html

抱歉!评论已关闭.