Boost Log : Attributes

Adding more information to log: Attributes

在前面的章节中,我们多次提到了属性和属性值。在这里,我们将发现如何使用属性向日志记录添加更多的数据。

每一个日志记录都可以包含许多命名的属性值。属性可以表示任何关于日志记录发生的条件的基本信息,比如代码中的位置,可执行的模块名,当前日期和时间,或者任何与您的特定应用程序和执行环境相关的数据。一个属性可以表现为一个值生成器,在这种情况下,它会为它所涉及的每个日志记录返回一个不同的值。只要属性生成该值,后者就会独立于创建者,并且可以被过滤器,格式化器和sinks使用。但是为了使用属性值,必须知道它的名称和类型,或者它可能有的至少一组类型。Log库中实现了许多常用属性,可以在文档中找到它们的值类型。

除此之外,正如设计概述部分所述,有三种可能的属性范围:日志源特定的,线程特定的和全局的。当创建日志记录时,来自这三个集合的属性值被合并为一个集合并传递给sinks。这意味着该属性的来源对sinks是没有影响的。任何属性都可以在任何范围内注册。当注册时,一个属性被赋予一个惟一的名称,以便使它能够搜索它。如果在几个范围内发现相同的命名属性,那么在任何进一步的处理中都将考虑到来自最特定范围的属性,包括过滤和格式化。这样的行为使您可以使用在局部中注册的日志来覆盖全局或线程范围的属性,从而减少线程的干扰。

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

Commonly used attributes

有一些在几乎任何应用程序中都有可能使用的属性。日志记录计数器和时间戳是很好的例子。它们可以通过一个单独的函数调用添加:

logging::add_common_attributes();

有了这个调用,属性“LineID”、“TimeStamp”、“ProcessID”和“ThreadID”会在全局范围中注册。LineID”属性是一个计数器,在每条记录产生时递增,第一个记录得到的是1。“TimeStamp”属性总是产生当前时间(例如,创建日志记录的时间,而不是写入到sink的时间)。最后两个属性标识了产生日志的进程和线程。

Note
在单线程构建中,“ThreadID”属性没有注册.

Tip
默认情况下,当应用程序启动时,Log库中没有注册任何属性。在开始写日志之前,应用程序必须在库中注册所有必需的属性。这可以作为库初始化的一部分来完成。读者可能会想知道日志是如何工作的。答案是,除了严重性级别,默认的sink不使用任何其他属性值来组成它的输出。这样做是为了避免对trivial logging进行任何初始化。一旦你使用了过滤器或格式器和非默认的sink,你就必须注册你需要的属性。

addcommonattributes函数是这里描述的几个便利助手之一。

一些属性在logger构造时自动注册。例如,severitylogger会注册一个log source级别的属性“Severity”,该属性可以用来为不同的日志记录添加严重等级。例如:

// We define our own severity levels
enum severity_level
{
    normal,
    notification,
    warning,
    error,
    critical
};

void logging_function()
{
    // The logger implicitly adds a source-specific attribute 'Severity'
    // of type 'severity_level' on construction
    src::severity_logger< severity_level > slg;

    BOOST_LOG_SEV(slg, normal) << "A regular message";
    BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
    BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
}

Tip
可以通过为这种类型定义操作符<<来定义自己的严重性级别的格式化规则。它将被库格式化程序自动使用。有关更多细节,请参阅这里

BOOST_LOG_SEV宏的作用与BOOST_LOG非常相似,除了它为logger的open_record方法提供了一个额外的参数。BOOST_LOG_SEV宏可以用下面替换:

void manual_logging()
{
    src::severity_logger< severity_level > slg;

    logging::record rec = slg.open_record(keywords::severity = normal);
    if (rec)
    {
        logging::record_ostream strm(rec);
        strm << "A regular message";
        strm.flush();
        slg.push_record(boost::move(rec));
    }
}

可以在这里看到,open_record可以采用命名参数。由库提供的一些logger type支持这些额外的参数,并且这种方法可以在编写自己的logger时使用。

More attributes

让我们看看在简单的例子中使用的add_common_attributes函数的引擎盖下面是什么。它可能是这样的:

void add_common_attributes()
{
    boost::shared_ptr< logging::core > core = logging::core::get();
    core->add_global_attribute("LineID", attrs::counter< unsigned int >(1));
    core->add_global_attribute("TimeStamp", attrs::local_clock());

    // other attributes skipped for brevity
}

这里counter和local_clock组件是属性类,它们继承自通用属性接口attribute。Log库提供了许多其他的属性类,包括function属性(调用获取值的函数对象)。例如,我们可以以类似的方式注册named_scope属性:

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

这使得每一个日志记录中能存储范围(scope)名。以下是它的用法:

void named_scope_logging()
{
    BOOST_LOG_NAMED_SCOPE("named_scope_logging");

    src::severity_logger< severity_level > slg;

    BOOST_LOG_SEV(slg, normal) << "Hello from the function named_scope_logging!";
}

日志源的属性与全局属性一样有用。严重性级别和通道名称是在日志源级别上最可能实现的属性。可以向Logger添加更多的属性,如下所:

void tagged_logging()
{
    src::severity_logger< severity_level > slg;
    slg.add_attribute("Tag", attrs::constant< std::string >("My tag value"));

    BOOST_LOG_SEV(slg, normal) << "Here goes the tagged record";
}

现在,通过这个logger完成的所有日志记录将被用这个特定的属性标记。这个属性值可以稍后用于过滤和格式化。

属性的另一个好的用处是能够标记应用程序不同部分所做的日志记录,以突出与单个进程相关的活动。我们甚至可以实现一个粗略的分析工具来检测性能瓶颈。例如:

void timed_logging()
{
    BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());

    src::severity_logger< severity_level > slg;
    BOOST_LOG_SEV(slg, normal) << "Starting to time nested functions";

    logging_function();

    BOOST_LOG_SEV(slg, normal) << "Stopping to time nested functions";
}

现在,logging_function函数或它所调用的任何其他函数的每一个日志记录都将包含“Timeline”属性自从该属性已经注册,该属性具有很高的精度。根据这些记录,将能够检测到代码的哪些部分需要或多或少执行时间。“Timeline”属性将在离开函数timed_logging的作用域时注销。

请参阅Attributes部分,以了解Log库提供的属性的详细描述。这一节的完整代码在本节最后。

Defining attribute placeholders

正如我们将在接下来的部分中看到的,定义一个关键字来描述应用程序使用的特定属性是很有用的。这个关键字将能够参与过滤和格式化表达式,就像我们在前几节中使用的severity占位符一样。例如,为了定义我们在前面的例子中使用的一些属性的占位符,我们可以这样写:

BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", attrs::named_scope::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", attrs::timer::value_type)

每个宏定义一个关键字。第一个参数是占位符名称,第二个参数是属性名,最后一个参数是属性值类型。一旦定义好了,占位符就可以在模板表达式和库的其他上下文中使用。关于定义属性关键字的更多细节可以在这里找到。

本节完整代码

/*
 *          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 <cstddef>
#include <string>
#include <ostream>
#include <fstream>
#include <iomanip>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/basic_logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

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;

// We define our own severity levels
enum severity_level
{
    normal,
    notification,
    warning,
    error,
    critical
};

BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", attrs::named_scope::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", attrs::timer::value_type)

void logging_function()
{
    src::severity_logger< severity_level > slg;

    BOOST_LOG_SEV(slg, normal) << "A regular message";
    BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
    BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
}

//[ example_tutorial_attributes_named_scope
void named_scope_logging()
{
    BOOST_LOG_NAMED_SCOPE("named_scope_logging");

    src::severity_logger< severity_level > slg;

    BOOST_LOG_SEV(slg, normal) << "Hello from the function named_scope_logging!";
}
//]

//[ example_tutorial_attributes_tagged_logging
void tagged_logging()
{
    src::severity_logger< severity_level > slg;
    slg.add_attribute("Tag", attrs::constant< std::string >("My tag value"));

    BOOST_LOG_SEV(slg, normal) << "Here goes the tagged record";
}
//]

//[ example_tutorial_attributes_timed_logging
void timed_logging()
{
    BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());

    src::severity_logger< severity_level > slg;
    BOOST_LOG_SEV(slg, normal) << "Starting to time nested functions";

    logging_function();

    BOOST_LOG_SEV(slg, normal) << "Stopping to time nested functions";
}
//]

// The operator puts a human-friendly representation of the severity level to the stream
std::ostream& operator<< (std::ostream& strm, severity_level level)
{
    static const char* strings[] =
    {
        "normal",
        "notification",
        "warning",
        "error",
        "critical"
    };

    if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
        strm << strings[level];
    else
        strm << static_cast< int >(level);

    return strm;
}

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

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    sink->set_formatter
    (
        expr::stream
            << std::hex << std::setw(8) << std::setfill('0') << line_id << std::dec << std::setfill(' ')
            << ": <" << severity << ">\t"
            << "(" << scope << ") "
            << expr::if_(expr::has_attr(tag_attr))
               [
                    expr::stream << "[" << tag_attr << "] "
               ]
            << expr::if_(expr::has_attr(timeline))
               [
                    expr::stream << "[" << timeline << "] "
               ]
            << expr::smessage
    );

    logging::core::get()->add_sink(sink);

    // Add attributes
    logging::add_common_attributes();
    logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
}

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

    named_scope_logging();
    tagged_logging();
    timed_logging();

    return 0;
}

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

时间: 2024-11-08 21:51:24

Boost Log : Attributes的相关文章

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支持包含本地字符集字符串的日志记录.基本上有两种方式做这件事.在 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在项目中应用

#pragma once #include <string> #include <boost/log/trivial.hpp> using std::string; #define LOG_DEBUG\ BOOST_LOG_SEV((MyLog::s_slg),(boost::log::trivial::debug)) #define LOG_INFO\ BOOST_LOG_SEV((MyLog::s_slg),(boost::log::trivial::info)) #defin

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 的示例程序时,遇到了下面的错误: zhifan$ make boost_log LDFLAGS='-lboost_log-mt' Undefined symbols for architecture x86_64: "boost::log::v2s_mt_posix::record_view::public_data::destroy(boost::log::v2s_mt_posix::record_view::public_data const*)", re

Boost log 简单使用

Boost log 简单使用 flyfish 2014-11-8 该示例是在VC2010 MFC Unicode环境下使用 内容包括 1 启动关闭日志 2 设置日志存储路径 3 设置输出日志等级 4 日志是否立即写入文件 5 设置单个文件的大小 6 设置磁盘最小可利用空间 Logger.h #pragma once #include <cassert> #include <iostream> #include <fstream> #include <boost/l

boost.log(九) 配置文件

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