【问题标题】:Cast a boost::log::expressions::attr< std::string > to std::string将 boost::log::expressions::attr< std::string > 转换为 std::string
【发布时间】:2015-09-18 18:08:57
【问题描述】:

在使用 Boost.Log 时,我试图保留我的 TimeStamp 格式化程序,例如:

  logging::add_file_log
    (
     keywords::file_name = "my.log",
     keywords::format =
     (
      expr::stream
      << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      << "," << expr::attr< int >("Line")
      << " " << expr::attr< std::string >("File")
      << " " << logging::trivial::severity
      << " - " << expr::smessage
     )
    );

据说我不能使用其他形式的格式化程序,因为我很难将"TimeStamp" 转换为我的自定义格式:

static void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
  strm << logging::extract< boost::posix_time::ptime >("TimeStamp", rec);

将输出类似:2015-Jul-01 16:06:31.514053,而我只对:"%Y-%m-%d %H:%M:%S" 感兴趣。但是第一种形式非常难以使用,例如我无法将expr::attr&lt; std::string &gt; 转换为简单的std::string 例如:

  logging::add_file_log
    (
     keywords::file_name = "my.log",
     keywords::format =
     (
      expr::stream
      << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
      << "," << expr::attr< int >("Line")
      << " " << boost::filesystem::path(expr::attr< std::string >("File"))
        .filename().string()
      << " " << logging::trivial::severity
      << " - " << expr::smessage
     )
    );

上面的代码甚至没有编译。

有没有一种简单的方法可以使用我的自定义格式打印TimeStamp,同时使用自定义转换为字符串以便能够使用boost::filesystem::path::filename()

【问题讨论】:

    标签: c++ boost boost-log


    【解决方案1】:

    在自定义格式化程序中,您可以轻松地以"%Y-%m-%d %H:%M:%S" 格式构建时间戳:

    void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
    {
        const boost::posix_time::ptime &pt = *logging::extract< boost::posix_time::ptime >("TimeStamp", rec);
        strm << pt.date() << " " << pt.time_of_day().hours() << ":" << pt.time_of_day().minutes() << ":" << pt.time_of_day().seconds()
        ...
        << rec[expr::smessage];
    }
    

    【讨论】:

    • 不使用需要使用setfill('0')?
    【解决方案2】:

    有多种方法可以实现您想要的。要理解的关键点是 Boost.Log 格式化表达式(以及过滤器,顺便说一句)是 Boost.Phoenix lambda 函数。因此,您可以使用 Boost.Phoenix 构造(例如 boost::phoenix::bind)在其中注入您自己的函数。例如,请参阅示例 here。您的代码将如下所示:

    std::string file_basename(logging::value_ref< std::string > const& filename)
    {
      // Check to see if the attribute value has been found
      if (filename)
        return boost::filesystem::path(filename.get()).filename().string();
      else
        return std::string();
    }
    
    // ...
    
    logging::add_file_log
    (
      keywords::file_name = "my.log",
      keywords::format =
      (
        expr::stream
          << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
          << "," << expr::attr< int >("Line")
          << " " << boost::phoenix::bind(&file_basename, expr::attr< std::string >("File"))
          << " " << logging::trivial::severity
          << " - " << expr::smessage
      )
    );
    

    另一种方法是使用属性关键字并为文件属性定义一个特定的operator&lt;&lt;。你可以找到一个例子here

    BOOST_LOG_ATTRIBUTE_KEYWORD(a_timestamp, "TimeStamp", boost::posix_time::ptime)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_line, "Line", int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_file, "File", std::string)
    
    namespace std {
    
    logging::formatting_ostream& operator<<
    (
      logging::formatting_ostream& strm,
      logging::to_log_manip< std::string, tag::a_file > const& manip
    )
    {
      strm << boost::filesystem::path(manip.get()).filename().string();
      return strm;
    }
    
    } // namespace std
    
    // ...
    
    logging::add_file_log
    (
      keywords::file_name = "my.log",
      keywords::format =
      (
        expr::stream
          << expr::format_date_time(a_timestamp, "%Y-%m-%d %H:%M:%S")
          << "," << a_line
          << " " << a_file
          << " " << logging::trivial::severity
          << " - " << expr::smessage
      )
    );
    

    请注意,属性关键字大大简化了表达式。

    最后,您可以使用wrap_formatter 将您自己的函数注入到流式表达式中。在格式化方面,wrap_formatter 调用您的函数,为它提供正在格式化的日志记录和格式化流。当您的函数返回时,包装器会自动返回对格式化流的引用,以便格式化表达式的其余部分可以继续进行。

    BOOST_LOG_ATTRIBUTE_KEYWORD(a_timestamp, "TimeStamp", boost::posix_time::ptime)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_line, "Line", int)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_file, "File", std::string)
    
    void file_basename(logging::record_view const& record, logging::formatting_ostream& strm)
    {
      // Check to see if the attribute value has been found
      logging::value_ref< std::string, tag::a_file > filename = record[a_file];
      if (filename)
        strm << boost::filesystem::path(filename.get()).filename().string();
    }
    
    // ...
    
    logging::add_file_log
    (
      keywords::file_name = "my.log",
      keywords::format =
      (
        expr::stream
          << expr::format_date_time(a_timestamp, "%Y-%m-%d %H:%M:%S")
          << "," << a_line
          << " " << expr::wrap_formatter(&file_basename)
          << " " << logging::trivial::severity
          << " - " << expr::smessage
      )
    );
    

    上面类似于boost::phoenix::bind 的第一个变体,但在file_basename 实现中允许更大的灵活性。

    【讨论】:

    • 您的第二个解决方案不起作用(在 fedora amd64 上使用 boost 1.55)。如果你创建函数static,编译器会告诉你operator&lt;&lt; 从未被使用过。我的猜测是 ADL 会先选择另一个 operator&lt;&lt;
    • 是的,操作符必须在被格式化的类型的命名空间中——在这种情况下是命名空间std。我已经更新了我的答案。
    猜你喜欢
    • 2016-01-28
    • 2015-09-03
    • 1970-01-01
    • 2020-03-28
    • 1970-01-01
    相关资源
    最近更新 更多