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