您的代码的主要问题是get_global_attributes()
返回全局属性集的副本。无论您对副本所做的更改都不会影响您看到的与日志记录相关的属性。如果你的代码是多线程的,那么首先使用全局属性是错误的,因为执行上下文是线程特定的。另外,假设attribute_cast
成功(即不返回空属性)假定该属性在程序中的任何地方都不会改变。您至少应该检查attribute_cast
的结果。
更好的方法来实现你想要的是将行和函数属性添加到Tracer构造函数中的线程特定属性集而不是日志初始化代码,并在析构函数中恢复其以前的状态。恢复部分很重要,您计划嵌套使用Tracer标记的通话。
using namespace boost::log;
class Tracer
{
enum restore_action
{
noop,
restore_prev,
remove
};
public:
Tracer(
Logger const& logger,
std::string const& function,
std::string const& file,
int line)
:
_logger(logger),
_function_name(function),
_prev_line_number(0),
_function(set_attribute("Function", function, _prev_function_name)),
_file(set_attribute("File", file, _prev_file_name)),
_line(set_attribute("Line", line, _prev_line_number))
{
BOOST_LOG_SEV(_logger, trivial::severity_level::trace)
<< "Entering " << _function_name;
}
~Tracer()
{
BOOST_LOG_SEV(_logger, trivial::severity_level::trace)
<< "Leaving " << _function_name;
restore_attribute(_function, _prev_function_name);
restore_attribute(_file, _prev_file_name);
restore_attribute(_line, _prev_line_number);
}
private:
template< typename T >
static std::pair< attribute_set::iterator, restore_action >
set_attribute(attribute_name name, T const& value, T& prev_value)
{
typedef attributes::mutable_constant<T> mutable_constant_t;
core_ptr p = core::get();
std::pair< attribute_set::iterator, bool > res =
p->add_thread_attribute(name, mutable_constant_t(value));
if (res.second)
return std::make_pair(res.first, remove);
mutable_constant_t prev =
attribute_cast<mutable_constant_t>(res.first->second);
if (!prev)
return std::make_pair(res.first, noop);
prev_value = prev.get();
prev.set(value);
return std::make_pair(res.first, restore_prev);
}
template< typename T >
static void restore_attribute(
std::pair< attribute_set::iterator, restore_action > const& state,
T const& prev_value)
{
typedef attributes::mutable_constant<T> mutable_constant_t;
switch (state.second)
{
case restore_prev:
{
mutable_constant_t attr =
attribute_cast<mutable_constant_t>(state.first->second);
if (attr)
attr.set(prev_value);
}
break;
case remove:
core::get()->remove_thread_attribute(state.first);
break;
case noop:
default:
break;
}
}
private:
Logger _logger;
std::string _function_name;
std::string _prev_function_name, _prev_file_name;
int _prev_line_number;
std::pair< attribute_set::iterator, restore_action > _function;
std::pair< attribute_set::iterator, restore_action > _file;
std::pair< attribute_set::iterator, restore_action > _line;
};
注意,该代码将标志着该线程与功能,文件和行所做的所有记录,因为它们是由示踪设置属性。如果这不是你真正想要的,你只需要标记关于进入和离开函数的消息,那么你应该添加函数,文件和行到记录器属性,而不是线程特定的。这将显着简化代码,因为您不必费心恢复以前的属性状态。
我还要补充说,有更好的工具来维护调用堆栈,特别是标记当前的执行上下文。 Named scopes允许您以比上面写的更便宜的方式标记当前范围或功能。该标记不会生成日志记录,但这很容易添加。
class Tracer
{
public:
Tracer(
Logger const& logger,
string_literal const& function,
string_literal const& file,
unsigned int line)
:
_logger(logger),
_function_name(function),
_scope_sentry(function, file, line)
{
BOOST_LOG_SEV(_logger, trivial::severity_level::trace)
<< "Entering " << _function_name;
}
~Tracer()
{
BOOST_LOG_SEV(_logger, trivial::severity_level::trace)
<< "Leaving " << _function_name;
}
private:
Logger _logger;
string_literal _function_name;
attributes::named_scope::sentry _scope_sentry;
};
相比原来代码这个版本有额外的限制,即功能和文件名必须是字符串文字,而不仅仅是任意的字符串。您还将不得不设置formatter。
谢谢你的回复,我 - 对不起 - 不完全grok。我的完整代码位于https://gist.github.com/floli/9f2c5de3fafebcda0d74 我的原始意图是将行号和文件添加到日志消息中。范围只是给你我打开范围的行号,而不是实际的BOOST_LOG_SEV调用。我认为这应该是日志库中的标准内容,并从用户那里入侵它。为了达到这个目的,我使用了第70行左右的宏。 接下来我想要这个跟踪器,它不能使用__LINE__和__FILE__,所以我想手动设置它。 – Horus
在这种情况下,我不明白你的问题。如果你想标记每个日志语句的位置,那么最好的办法是使用'add_value'操纵器(http://www.boost.org/doc/libs/1_59_0/libs/log/doc/html /log/detailed/utilities.html#log.detailed.utilities.manipulators.add_value)。要自动化它的使用,你可以定义你自己的日志宏,它会包装'BOOST_LOG_SEV'和'add_value'并在整个代码中使用它。这不可能作为一个特殊的属性或记录器来实现。 –
这个地方,因为它的信息长度非常有限,似乎并不适合讨论这个问题。你能否到达其他地方? – Horus