Boost Log:设置属性使日志条目消失

时间:2015-10-14 10:44:20

标签: logging boost boost-log

我有一个跟踪器,应该记录进出函数:

class Tracer
{
public:  
  Tracer(Logger logger, std::string function, std::string file)
    :
    _logger(logger),
    _function(function),
    _file(file)
  {}

  ~Tracer()
  {
    using namespace boost::log;
    // attribute_cast<attributes::mutable_constant<int>>(core::get()->get_global_attributes()["Line"]).set(1);
    attribute_cast<attributes::mutable_constant<std::string>>(core::get()->get_global_attributes()["File"]).set(_file);
    attribute_cast<attributes::mutable_constant<std::string>>(core::get()->get_global_attributes()["Function"]).set(_function);
    BOOST_LOG_SEV(_logger, trivial::severity_level::trace) << "Leaving " << _function;
  }

private:
  Logger _logger;
  std::string _function;
  std::string _file;
};

这样做很好,但是一旦我取消注释设置Line attribut的行,我就不会再收到任何Leaving消息了。

预先添加属性:

using namespace boost::log;
add_common_attributes();
core::get()->add_global_attribute("Scope", attributes::named_scope());
core::get()->add_global_attribute("Rank", attributes::constant<int>(5));
core::get()->add_global_attribute("Line", attributes::mutable_constant<int>(5));
core::get()->add_global_attribute("File", attributes::mutable_constant<std::string>(""));
core::get()->add_global_attribute("Function", attributes::mutable_constant<std::string>(""));

设置所有其他属性,类似于Line工作正常。我感到困惑,因为Line似乎不是一个保留名称,它在其他地方工作,boost :: log源显示该名称没有相关用法。

这可能是什么问题?我很高兴向任何人发送complate代码(总共大约150行)

1 个答案:

答案 0 :(得分:0)

代码的主要问题是get_global_attributes()返回全局属性集的副本。无论您对副本所做的任何更改都不会影响您在日志记录中看到的属性。如果您的代码是多线程的,那么首先使用全局属性是错误的,因为执行上下文是特定于线程的。此外,假设attribute_cast成功(即不返回空属性),则假定该属性在程序中的任何位置都不会更改。您至少应该检查attribute_cast的结果。

实现所需内容的更好方法是将Line和Function属性添加到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;
};

请注意,此代码将使用Tracer设置的Function,File和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