【发布时间】:2016-05-06 11:00:25
【问题描述】:
我在我的应用程序中使用 boost log,虽然配置起来很棘手,但它通常运行良好。
但是,现在我想在我的应用程序中添加一些更高级的过滤逻辑,但我想不通。
我想要的是有两个“级别”的过滤:
- 我已经在使用具有不同级别的“严重性记录器”,例如
debug、warn、note等。这是设置和工作。 - 我想添加一种额外的方法来过滤记录,方法是查看记录源自的“命名范围”。
例如,我希望能够仅查看严重性 >= note 的记录,以及在 monthly 的 NAMED_SCOPE 内的记录。
我已经成功使用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: http://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
【问题讨论】:
-
我怀疑问题出在绑定上。
attrs::named_scope::get_scopes()实际上在设置过滤器时将 lambda 表达式绑定到范围列表。相反,在我看来,should be placeholder 在调用时提取它。 -
嗯听起来很有可能。我将调用签名更改为:
bool my_filter(boost::log::value_ref< attrs::named_scope > const& theNamedScope),并将绑定更改为boost::phoenix::bind(&my_filter, expr::attr< attrs::named_scope >("Scope").or_none())。它可以编译,但我有 2 个问题:1)在调试器中停止,theNamedScope参数始终为 NULL,2)我无法弄清楚如何在my_filter(...)函数中访问 named_scope_list。 -
好的,我会试着玩一下,现在你让我好奇了:) 同时,你能把更新的代码添加到你的问题中吗?保留原件,只需创建一个新部分。
-
一个问题 - 你是如何让
add_console_log编译的?特别是<< "[" << named_scope << "] "部分——这里的named_scope 是什么(带有命名空间的完整类型)?我需要在那里使用expressions::format_named_scope(...)。 -
我的 MyLogger.h 文件中有这个:
BOOST_LOG_ATTRIBUTE_KEYWORD(named_scope, "Scope", attrs::named_scope::value_type)。当我设置这个库和 MyLogger 类时,我非常困惑,所以我怀疑我可能以一种非常奇怪的方式做到了。我仍在尝试制作一个完全可以工作的最小示例,但正在苦苦挣扎。