Boost Log : Setting up sinks

Setting up sinks

有时候,trivial(简单的)logging并不能满足要求。例如,想要更精细的日志处理,而不是简单地打印出来。为此,必须构建自定义的sinks,并且将它们注册到core中。这通常只在程序的开始执行一次。

注意:
特别要提醒的是,在前面的章节中我们并没有初始化任何sinks,但trivial logging任然能够正常运行。这是因为,在用户没有设置任何sinks时,log库会使用一个默认的sink。这个默认的sink一般来说是将日志按照固定的格式打印到console,就像我们在前面所看到的一样。提供这样一个默认sink,是为了在没有做任何初始化时,trivial logging也能立即使用。一旦core中添加了任何其他的sinks,默认的sink就不再起作用。但trivial logging的宏任然能够使用

File logging unleashed

作为开始,下面是如何初始化一个log,让日志记录出输出到一个文件:

void init()
{
    logging::add_file_log("sample.log");

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}

 增加的一行是 add_file_log 函数。正如名字所示,这个函数初始化一个logging sink,使其将日志记录写到一个文件中。这个函数还接受一些自定义选项,例如文件rotation(轮转)周期和大小限制。例如:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",                  //1
        keywords::rotation_size = 10 * 1024 * 1024,             //2
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),  //3
        keywords::format = "[%TimeStamp%]: %Message%" //4
    );

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}    

1. 文件名模板
2. rotation在每个文件到达10M
3. ... 或到达0点
4. 日志格式

完整代码:

/*
 *          Copyright Andrey Semashev 2007 - 2015.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */

#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>

namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;

#if 0

//[ example_tutorial_file_simple
void init()
{
    logging::add_file_log("sample.log");

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}
//]

// We need this due to this bug: https://svn.boost.org/trac/boost/ticket/4416
//[ example_tutorial_file_advanced_no_callouts
void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        keywords::rotation_size = 10 * 1024 * 1024,
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
        keywords::format = "[%TimeStamp%]: %Message%"
    );

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}
//]

#else

//[ example_tutorial_file_advanced
void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",                                        /*< file name pattern >*/
        keywords::rotation_size = 10 * 1024 * 1024,                                   /*< rotate files every 10 MiB... >*/
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), /*< ...or at midnight >*/
        keywords::format = "[%TimeStamp%]: %Message%"                                 /*< log record format >*/
    );

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}
//]

#endif

int main(int, char*[])
{
    init();
    logging::add_common_attributes();

    using namespace logging::trivial;
    src::severity_logger< severity_level > lg;

    BOOST_LOG_SEV(lg, trace) << "A trace severity message";
    BOOST_LOG_SEV(lg, debug) << "A debug severity message";
    BOOST_LOG_SEV(lg, info) << "An informational severity message";
    BOOST_LOG_SEV(lg, warning) << "A warning severity message";
    BOOST_LOG_SEV(lg, error) << "An error severity message";
    BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";

    return 0;
}
View Code

可以看到,函数的可选参数是以命名参数方式传递的。在log库的其他地方,也有许多这种处理方式。你会习惯的。这些参数的意义基本上都能自我解释,并且在手册中有说明(此处应有链接)。本节将介绍这个和其他一些初始化函数。

注意:
你可以注册不止一个sink。每个sink都会独立地接收并处理传递给它的日志。

Sinks in depth: More sinks

如果你不想了解更多细节,可以跳过这一节继续往下阅读。然而,如果你需要对sink配置进行更全面的控制,或者希望使用比通过helper函数提供的更多种类的接收器,你可以手动注册sinks。

上面调用的函数 add_file_log,其内容可以简化如下:

void init()
{
    // Construct the sink
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    // Add a stream to write log to
    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    // Register the sink in the logging core
    logging::core::get()->add_sink(sink);
}

完整代码:

/*
 *          Copyright Andrey Semashev 2007 - 2015.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */

#include <fstream>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>

namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;

//[ example_tutorial_file_manual
void init()
{
    // Construct the sink
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    // Add a stream to write log to
    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    // Register the sink in the logging core
    logging::core::get()->add_sink(sink);
}
//]

int main(int, char*[])
{
    init();

    src::logger lg;
    BOOST_LOG(lg) << "Hello world!";

    return 0;
}
View Code

首先,你可能注意到,每个sink由两个class组成:frontend 和 backend。The frontend(上面代码中的 synchronous_sink)负责所有sinks的各种常见任务,例如线程同步模型、过滤,以及基于文本的接收器的格式化。The backend(上面代码中的text_ostream_backend)实现所有特定的sink,例如当前的写入文件。Log库提供了多种前端和后端,都可以直接使用。

上面的synchronous_sink类模板表明sink是同步的,它允许多个线程同时写日志,并在资源竞争时阻塞。这意味着后端的 text_ostream_backend 根本不需要担心多线程问题。

The text_ostream_backend类,将格式化的日志记录写入与stl兼容的stream中。我们使用了上面的file stream,但是我们可以使用任何类型的stream。例如,可以如下所示,将输出添加到控制台:

#include <boost/core/null_deleter.hpp>

// We have to provide an empty deleter to avoid destroying the global stream object
boost::shared_ptr< std::ostream > stream(&std::clog, boost::null_deleter());
sink->locked_backend()->add_stream(stream);

The text_ostream_backend 支持添加多个stream。在这种情况下,它的输出将被复制到所有添加的stream中。将输出复制到控制台和文件是很有用的,因为库的所有过滤、格式化和其他开销在每个记录中只发生一次。

注意:
请注意注册几个不同的sink和注册一个有几个目标流的sink之间的区别。虽然前者允许对每个sink进行独立的定制输出,但是如果不需要这样的定制,后者将工作得更快。这个特性是特定于这个特定的后端。

Log库提供了许多提供不同的日志处理逻辑的后端。例如,通过指定syslog后端,可以通过网络将日志记录发送到syslog服务器,或者通过设置Windows NT事件日志后端,你可以使用标准的Windows工具在运行时监视应用程序。

最后值得注意的,是访问sink后端的locked_backend。它用于获得对后端的线程安全访问,并且所有sink前端都提供这个函数。这个函数返回一个指向后端的智能指针,只要它存在,后端就被锁定(这意味着,即使另一个线程试图记录日志,并且日志记录已经被传递到sink,这条日志也不会被处理,直到你释放了后端)。唯一的例外是unlocked_sink前端,它根本不同步,只是返回一个未锁定的指向后端的指针。

原文地址:https://www.cnblogs.com/kohlrabi/p/9136435.html