我在我的应用程序中使用boost log,虽然配置起来很棘手,但它通常运行良好.
现在,我想在我的应用程序中添加一些更高级的过滤逻辑,我无法弄明白.
我想要的是有两个“级别”的过滤:
>我已经使用了不同级别的“严重记录器”,如调试,警告,注意等.这是设置和工作.
>我想通过查看记录发出的“命名范围”来添加另一种过滤记录的方法.
因此,我希望能够在每月的NAMED_SCOPE中仅查看严重性为> = note,AND的记录.
我已经成功地使用了BOOST_LOG_NAMED_SCOPE()宏,并且可以在日志消息中看到范围堆栈.
我尝试用boost :: phoenix实现自定义过滤器,但我无法让它工作.
我在这里粘贴的代码在我的应用程序中编译(我正在努力剥离它,所以我可以在这里包含一个完整的工作最小示例),但是my_filter(..)函数中似乎没有任何事情发生.我认为我没有正确地将范围堆栈传递给绑定函数,因为如果我在范围堆栈中的循环中放置一个std :: cout语句,我看不到任何打印.
这里最小的例子:
// Excerpt from MyLogger.cpp class
bool my_filter(attrs::named_scope_list const& scopeList) {
for (attrs::named_scope_list::const_iterator iter = scopeList.begin(); iter != scopeList.end(); ++iter) {
if ( (*iter).scope_name == "monthly") {
return true;
}
}
return false;
}
void setup_logging(std::string lvl) {
logging::core::get()->add_global_attribute(
"Scope", attrs::named_scope()
);
logging::add_console_log(
std::clog,
keywords::format = (
expr::stream
<< "[" << severity << "] "
<< "[" << named_scope << "] "
<< expr::smessage
)
);
try {
// set the severity level...
EnumParser<severity_level> parser;
logging::core::get()->set_filter(
(
severity >= parser.parseEnum(lvl) &&
( expr::has_attr("Scope") && ( boost::phoenix::bind(&my_filter, attrs::named_scope::get_scopes() ) ) )
)
);
} catch (std::runtime_error& e) {
std::cout << e.what() << std::endl;
std::cout << "'" << lvl << "' is an invalid --log-level! Must be one of "
<< "[debug, info, note, warn, err, fatal]\n";
exit(-1);
}
}
编辑更新了最小的工作示例:
TEMLogger.h
#ifndef _TEMLOGGER_H_
#define _TEMLOGGER_H_
#include <string>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <exception>
#include <map>
#include <iomanip>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/attributes/current_process_id.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
/** Define the "severity levels" for Boost::Log's severity logger. */
enum severity_level {
debug, info, note, warn, err, fatal
};
/** Convert from string to enum integer value.
*
* Inspired by: https://stackoverflow.com/questions/726664/string-to-enum-in-c
*/
template <typename T>
class EnumParser {
std::map <std::string, T> enumMap;
public:
EnumParser(){};
T parseEnum(const std::string &value) {
typename std::map<std::string, T>::const_iterator iValue = enumMap.find(value);
if (iValue == enumMap.end())
throw std::runtime_error("Value not found in enum!");
return iValue->second;
}
};
BOOST_LOG_GLOBAL_LOGGER(my_logger, src::severity_logger< severity_level >);
/** Send string representing an enum value to stream
*/
std::ostream& operator<< (std::ostream& strm, severity_level lvl);
void setup_logging(std::string lvl);
#endif /* _TEMLOGGER_H_ */
TEMLogger.cpp
#include <boost/log/expressions/formatters/named_scope.hpp>
#include <boost/log/expressions.hpp>
#include <boost/phoenix.hpp>
#include "TEMLogger.h"
// Create the global logger object
src::severity_logger< severity_level > glg;
// Add a bunch of attributes to it
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(named_scope, "Scope", attrs::named_scope::value_type)
/** Initialize the enum parser map from strings to the enum levels.*/
template<>
EnumParser< severity_level >::EnumParser() {
enumMap["debug"] = debug;
enumMap["info"] = info;
enumMap["note"] = note;
enumMap["warn"] = warn;
enumMap["err"] = err;
enumMap["fatal"] = fatal;
}
std::ostream& operator<< (std::ostream& strm, severity_level level) {
static const char* strings[] = {
"debug", "info", "note", "warn", "err", "fatal"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
bool my_filter(boost::log::value_ref< attrs::named_scope > const& theNamedScope) {
// I think I want something like this:
// for (attrs::named_scope_list::const_iterator iter = scopeList.begin(); iter != scopeList.end(); ++iter) {
// if ( (*iter).scope_name == "monthly"){
// return true;
// }
// }
return true;
}
void setup_logging(std::string lvl) {
logging::core::get()->add_global_attribute(
"Scope", attrs::named_scope()
);
logging::add_console_log(
std::clog,
keywords::format = (
expr::stream
<< "[" << severity << "] "
<< "[" << named_scope << "] "
<< expr::smessage
)
);
try {
// set the severity level...
EnumParser<severity_level> parser;
logging::core::get()->set_filter(
(
severity >= parser.parseEnum(lvl) &&
( expr::has_attr("Scope") && ( boost::phoenix::bind(&my_filter, expr::attr< attrs::named_scope >("Scope").or_none()) ) )
)
);
} catch (std::runtime_error& e) {
std::cout << e.what() << std::endl;
std::cout << "'" << lvl << "' is an invalid --log-level! Must be one of "
<< "[debug, info, note, warn, err, fatal]\n";
exit(-1);
}
}
主要计划
#include "TEMLogger.h"
extern src::severity_logger< severity_level > glg;
void func1() {
BOOST_LOG_NAMED_SCOPE("monthly");
for (int i=0; i<5; ++i) {
BOOST_LOG_SEV(glg, note) << "doing iteration " << i << "within monthly scope!";
}
}
int main(int argc, char* argv[]) {
std::cout << "Setting up logging...\n";
setup_logging("debug");
BOOST_LOG_SEV(glg, note) << "Some message in the main scope";
func1();
BOOST_LOG_SEV(glg, note) << "Another message in the main scope";
return 0;
}
编
(我在Mac上,由于我安装Boost的方式,我必须指定编译器,以及链接Boost库的方法.YMMV)
g++-4.8 -o TEMLogger.o -c -g -DBOOST_ALL_DYN_LINK TEMLogger.cpp
g++-4.8 -o log-filter-example.o -c -g -DBOOST_ALL_DYN_LINK log-filter-example.cpp
g++-4.8 -o a.out log-filter-example.o TEMLogger.o -L/usr/local/lib -lboost_system-mt -lboost_filesystem-mt -lboost_thread-mt -lboost_log-mt
跑
$./a.out
Setting up logging...
[note] [] Some message in the main scope
[note] [monthly] doing iteration 0within monthly scope!
[note] [monthly] doing iteration 1within monthly scope!
[note] [monthly] doing iteration 2within monthly scope!
[note] [monthly] doing iteration 3within monthly scope!
[note] [monthly] doing iteration 4within monthly scope!
[note] [] Another message in the main scope
最佳答案 分析
您的问题在于如何使用phoenix::bind
创建过滤表达式.
boost::phoenix::bind(&my_filter
, attrs::named_scope::get_scopes())
它的编写方式,它将绑定到绑定时get_scopes()返回的值.相反,我们需要延迟评估,这将在为每条消息调用my_filter函数之前发生.这是通过boost::log::expressions::attribute_actor
完成的,我们可以使用boost::log::expressions::attr(...)
创建
boost::phoenix::bind(&my_filter
, expr::attr<attrs::named_scope>("Scope").or_none())
下一期是boost::log::attributes::named_scope
.正如文件所说
The basic_named_scope attribute is essentially a hook to the thread-specific instance of scope list.
我们实际上对提取给定消息的当前作用域堆栈感兴趣,而不是这个虚拟属性.根据value_type,这是boost::log::attributes::named_scope_list
的实例.因此,我们应该将代码更改为
boost::phoenix::bind(&my_filter
, expr::attr<attrs::named_scope_list>("Scope").or_none())
并调整my_filter(…)的签名以匹配:
bool my_filter(boost::log::value_ref<attrs::named_scope_list> const& scopes)
现在,由于我们使用.or_none()来创建attribute_actor,我们可以从过滤器表达式中删除对“Scope”属性的检查
expr::has_attr("Scope") // This goes away
而是在我们的过滤功能中进行此测试
if (!scopes.empty()) { // ...
} else { return false; }
最后,我们应该有办法配置我们想要过滤的范围.所以,让我们在过滤函数中添加一个参数:
bool my_filter(boost::log::value_ref<attrs::named_scope_list> const& scopes
, std::string const& target_scope)
{ // ...
}
并将其绑定到所需的值
std::string target_scope("scope_2"); // Or read from config
// .....
(boost::phoenix::bind(&my_filter
, expr::attr<attrs::named_scope_list>("Scope").or_none()
, target_scope))
示例代码
包含,类型定义:
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
// NB: The following two are just convenience
// Reduce them to just the headers needed to reduce compile time
#include <boost/log/attributes.hpp>
#include <boost/log/expressions.hpp>
#include <boost/phoenix.hpp>
// ============================================================================
namespace bl = boost::log;
// ----------------------------------------------------------------------------
typedef bl::sources::severity_logger <bl::trivial::severity_level> logger_t;
typedef bl::trivial::severity_level log_level;
// ----------------------------------------------------------------------------
BOOST_LOG_ATTRIBUTE_KEYWORD(my_named_scope, "Scope", bl::attributes::named_scope::value_type)
// ============================================================================
过滤功能:
// ============================================================================
bool my_filter(bl::value_ref<bl::attributes::named_scope_list> const& scopes
, std::string const& target_scope)
{
bool matched(false);
if (!scopes.empty()) {
for (auto& scope : scopes.get()) {
if (scope.scope_name == target_scope) {
matched = matched || true; // Any scope name matches...
}
}
}
return matched;
}
// ============================================================================
记录器初始化:
// ============================================================================
void init_logging()
{
bl::core::get()->add_global_attribute(
"Scope", bl::attributes::named_scope()
);
bl::add_console_log(std::clog
, bl::keywords::format = (
bl::expressions::stream
<< "[" << bl::trivial::severity << "] "
<< "[" << my_named_scope << "] "
// Alternative way to format this:
// << bl::expressions::format_named_scope("Scope", bl::keywords::format = "[%n] ")
<< bl::expressions::smessage
));
// Hard-coded, determine this as appropriate from config
log_level target_severity(log_level::info);
std::string target_scope("scope_2");
bl::core::get()->set_filter(
(bl::trivial::severity >= target_severity)
&& (boost::phoenix::bind(&my_filter
, bl::expressions::attr<bl::attributes::named_scope_list>("Scope").or_none()
, target_scope))
);
}
// ============================================================================
最后测试一下:
// ============================================================================
void log_it(logger_t& log, int n)
{
BOOST_LOG_SEV(log, log_level::debug) << "A" << n;
BOOST_LOG_SEV(log, log_level::trace) << "B" << n;
BOOST_LOG_SEV(log, log_level::info) << "C" << n;
BOOST_LOG_SEV(log, log_level::warning) << "D" << n;
BOOST_LOG_SEV(log, log_level::error) << "E" << n;
BOOST_LOG_SEV(log, log_level::fatal) << "F" << n;
}
// ============================================================================
int main()
{
init_logging();
logger_t log;
log_it(log, 1);
{
BOOST_LOG_NAMED_SCOPE("scope_1");
log_it(log, 2);
}
{
BOOST_LOG_NAMED_SCOPE("scope_2");
log_it(log, 3);
{
BOOST_LOG_NAMED_SCOPE("scope_3");
log_it(log, 4);
}
log_it(log, 5);
}
return 0;
}
// ============================================================================
测试运行
没有过滤的输出:
[debug] [] A1
[trace] [] B1
[] C1
[] D1
[error] [] E1
[fatal] [] F1
[debug] [scope_1] A2
[trace] [scope_1] B2
[scope_1] C2
[scope_1] D2
[error] [scope_1] E2
[fatal] [scope_1] F2
[debug] [scope_2] A3
[trace] [scope_2] B3
[scope_2] C3
[scope_2] D3
[error] [scope_2] E3
[fatal] [scope_2] F3
[debug] [scope_2->scope_3] A4
[trace] [scope_2->scope_3] B4
[scope_2->scope_3] C4
[scope_2->scope_3] D4
[error] [scope_2->scope_3] E4
[fatal] [scope_2->scope_3] F4
[debug] [scope_2] A5
[trace] [scope_2] B5
[scope_2] C5
[scope_2] D5
[error] [scope_2] E5
[fatal] [scope_2] F5
带过滤的输出(如示例代码中,仅信息级别和更高级别,以及仅具有名为“scope_2”的某些范围的输出):
[scope_2] C3
[scope_2] D3
[error] [scope_2] E3
[fatal] [scope_2] F3
[scope_2->scope_3] C4
[scope_2->scope_3] D4
[error] [scope_2->scope_3] E4
[fatal] [scope_2->scope_3] F4
[scope_2] C5
[scope_2] D5
[error] [scope_2] E5
[fatal] [scope_2] F5