将 boost::log::expressions::attr< std::string > 转换为 std::string

4
当我使用Boost.Log时,我试图保持我的 时间戳格式化程序如下:
  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< std::string >转换为简单的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()

2个回答

5

在自定义格式化程序中,您可以轻松地构建时间戳,格式为"%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') 吗?不需要。 - malat

4

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

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<<。你可以在这里找到一个例子(链接)

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<< 从未被使用。我猜测 ADL 会首先选择另一个 operator<< - malat
1
是的,在格式化类型所在的命名空间中必须使用操作符 - 在这种情况下是 std 命名空间。我已更新我的答案。 - Andrey Semashev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接