Boost Log : Log record formatting

Log record formatting

如果您尝试运行前几节中的示例,您可能已经注意到,只有日志记录消息被写到文件中。当没有设置格式器(formatter)时,这是Log库的默认行为。除非指定格式器,否则即使向logging core或logger添加了属性,属性值将无法输出。回到前面的教程章节中的一个例子:

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
    );
}

对于add_file_log函数,format参数允许指定日志记录的格式。如果喜欢手动设置sink,那么sink前端会为此提供set_formatter成员函数。

如前所述,可以通过多种方式指定格式。

Lambda-style formatters

可以使用如下所示的lambda样式的表达式创建格式器:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        // This makes the sink to write log records that look like this:
        // 1: <normal> A normal severity message
        // 2: <error> An error severity message
        keywords::format =
        (
            expr::stream
                << expr::attr< unsigned int >("LineID")
                << ": <" << logging::trivial::severity
                << "> " << expr::smessage
        )
    );
}

完整代码
在这里,stream是格式化输出日志记录的stream的占位符。其他插入参数,例如attr和message,是定义应该在stream中存储什么内容的操作器。我们已经在过滤表达式中看到了severity占位符,现在,它又在格式器中被使用。这是一个很好的统一:可以在过滤器和格式器中使用相同的占位符。attr占位符与severity占位符相似,因为它也表示属性值。不同之处是,severity占位符表示名称为“Severity”这个特定属性,其类型为trivial::severity_level,但attr可以用来表示任何属性。在其他方面这两个占位符是等价的。例如,可以用以下方法替代severity :

expr::attr< logging::trivial::severity_level >("Severity")

Tip
如前一节所示,可以为用户的属性定义类似severity这样的占位符。作为使用更简单的模板表达式的好处,此类占位符允许在占位符定义中包含所有关于属性(名称和值类型)的信息。这使得编码更不容易出错(不会拼写错误的属性名称或指定不正确的值类型),因此推荐使用这种方式定义新属性并在模板表达式中使用它们。

还有其他的格式化程序操纵器可以提供对日期的高级支持,时间和其他类型。一些操纵器接受附加参数,用来自定义其行为。这些参数中大多数都是命名的,可以通过 Boost.Parameter风格传递。
做一些改变,让我们看看手动初始化sink是如何完成的:

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
               // line id will be written in hex, 8-digits, zero-filled
            << std::hex << std::setw(8) << std::setfill('0') << expr::attr< unsigned int >("LineID")
            << ": <" << logging::trivial::severity
            << "> " << expr::smessage
    );

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

完整代码
可以看到,可以在表达式中绑定格式更改操作器;就像streams,当后续的日志记录被格式化时,这些操作器将影响其属性值格式。更多的操作器在Detailed features description部分被描述。

Boost.Format-style formatters

作为另一种选择,您可以使用类似Boost.Format的语法来定义格式器。如上所述的格式器可以如下所示:

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"));

    // This makes the sink to write log records that look like this:
    // 1: <normal> A normal severity message
    // 2: <error> An error severity message
    sink->set_formatter
    (
        expr::format("%1%: <%2%> %3%")
            % expr::attr< unsigned int >("LineID")
            % logging::trivial::severity
            % expr::smessage
    );

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

完整代码
format占位符接受带有(参数的)位置说明的格式化字符串。注意,目前只支持位置格式。add_file_log和类似函数可以使用相同的格式规范。

Specialized formatters

Log库为许多类型(如日期、时间和作用域)提供专门的格式器。这些格式器对格式化后的值提供扩展控制。例如,可以使用与Boost.DateTime兼容的格式字符串来描述日期和时间格式:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        // This makes the sink to write log records that look like this:
        // YYYY-MM-DD HH:MI:SS: <normal> A normal severity message
        // YYYY-MM-DD HH:MI:SS: <error> An error severity message
        keywords::format =
        (
            expr::stream
                << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
                << ": <" << logging::trivial::severity
                << "> " << expr::smessage
        )
    );
}

完整代码
同样的formatter也可以在Boost.Format-style formatter上下文中使用。

String templates as formatters

在某些情况下,文本模板也可以作为格式器。在这种情况下,将调用Log库初的始化支持代码来解析模板并重构适当的格式器。当使用这种方法时,需要注意一些事项,但是这里我们只简单地描述一下模板格式。

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        keywords::format = "[%TimeStamp%]: %Message%"
    );
}

完整代码
模板可能包含许多被百分号(%)包含的占位符。每个占位符必须包含要插入的属性值名称,而不是占位符。日志记录消息将替换%Message%占位符。

Note
sink后端的set_formatter方法中不接受文本格式模板。

Custom formatting functions

您可以向支持格式化的接收器后端添加自定义格式器。格式器实际上是一个函数对象,支持以下签名:

void (logging::record_view const& rec, logging::basic_formatting_ostream< CharT >& strm);

这里的CharT是目标字符类型。当日志记录视图rec通过过滤并存储在日志中时,将调用格式器。

Tip
记录视图与记录非常相似。值得注意的区别是,视图是不可变的,并且实现了浅拷贝。格式器和sinks只能对记录视图进行操作,这阻止它们修改记录,从而其他线程中的其他sinks仍然可以使用记录视图。

格式化的记录应该通过插入到与stl兼容的输出流strm中来构建。下面是一个自定义格式化程序函数使用的示例:

void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
    // Get the LineID attribute value and put it into the stream
    strm << logging::extract< unsigned int >("LineID", rec) << ": ";

    // The same for the severity level.
    // The simplified syntax is possible if attribute keywords are used.
    strm << "<" << rec[logging::trivial::severity] << "> ";

    // Finally, put the record message to the stream
    strm << rec[expr::smessage];
}

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(&my_formatter);

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

完整代码

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