如何将 boost::log::表达式格式化程序对象插入到流中以格式化时间戳



我正在按照有关 Boost.log 记录格式的教程进行操作,我正在尝试格式化时间戳以仅在秒的小数部分显示 2 位数字。 9年前,这里提出了同样的问题,如何自定义Boost.Log的"时间戳"格式,但该解决方案似乎不适用于最新版本的Boost。这是我尝试过的,它类似于上述解决方案,但使用format_date_time< boost::posix_time::ptime >而不是date_time<boost::posix_time::ptime>

namespace expr = boost::log::expressions;
auto ts = expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.");
auto ts_fractional = expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%f");
auto ts_fractional_short = (expr::format("%.2s") % ts_fractional);

auto strm = expr::stream
<< '[' << ts << '.'
<< ts_fractional_short << ']'   //error here. ts_fractional would work
<< '[' << logging::trivial::severity << ']'
<< " " << expr::smessage;
sink->set_formatter(strm);

似乎相关的错误是:

/path/to/boost_1_73_0/boost/log/utility/formatting_ostream.hpp:921:19: note:   'boost::log::v2s_mt_posix::basic_formatting_ostream<char>::ostream_type {aka std::basic_ostream<char>}' is not derived from 'boost::log::v2s_mt_posix::basic_formatting_ostream<CharT, TraitsT, AllocatorT>'
strm.stream() << value;
...
/path/to/boost_1_73_0/boost/log/utility/formatting_ostream.hpp:921:19: note:   cannot convert 'value' (type 'const boost::log::v2s_mt_posix::aux::basic_format<char>::pump') to type 'const id& {aka const boost::log::v2s_mt_posix::aux::id<boost::log::v2s_mt_posix::aux::process>&}'
strm.stream() << value;

有没有办法将expr::format()返回的对象转换为可以注入expr::stream的对象?

更新

我找到了一个解决方案,即使用自定义格式化程序,该格式化程序采用formatting_ostreamrecord_view,并从记录中提取时间戳。 然后,我使用local_date_time对象和output_facets 进行格式化,最后使用秒小数字符串上的boost::format将其写回 ostream。 它非常丑陋; 一定有更好的方法。

void formatter(const boost::log::record_view &rec, boost::log::formatting_ostream &os)
{
auto pt = logging::extract< boost::posix_time::ptime >("TimeStamp", rec);
using namespace boost::local_time;
using namespace boost::gregorian;
stringstream ss;
auto output_facet = new local_time_facet();
auto input_facet = new local_time_input_facet();
ss.imbue(locale(locale::classic(), output_facet));
ss.imbue(locale(ss.getloc(), input_facet));
local_date_time ldt(not_a_date_time);
ss << pt;
ss >> ldt;    //yuck...
output_facet->format("%Y-%m-%d %H:%M:%S");
ss.str("");
ss << ldt;
auto ts = ss.str();
output_facet->format("%f");
ss.str("");
ss << ldt;
auto ts_fractional = ss.str();
os << boost::format("[%1%.%2%][%3%] %4%")
% ts
% (boost::format("%.3s") % ts_fractional)
% logging::extract< boost::log::trivial::severity_level >("Severity", rec)
% rec[expr::smessage];
}
int main(int argc, char *argv[]) {
...
sink->set_formatter(&formatter);
...
}

更新 2

以防万一有人偶然发现这一点并想将@sehe的format_f与 C++11 编译器一起使用:

struct format_f {
std::string format_str;
template<typename T1, typename... Ts>
string operator()(T1 const& t1, Ts const&... ts) const {
return consume_arg((boost::format(format_str) % t1), ts...).str();
}
template<typename T1>
T1 consume_arg(T1 &t1) const {
return t1;
}
template<typename T1, typename T2, typename... Ts>
T1 consume_arg(T1 &t1, T2 const& t2, Ts const&... ts) const {
return consume_arg(t1 % t2, ts...);
}
};

您希望具有延迟的可调用对象。

从我的这个旧答案中松散地借用Xfrm,我建议做一个完整日期时间的子字符串:

static constexpr Xfrm Left24 { [](std::string const& s) { return s.substr(0, 24); } };
logging::formatter formatter = expr::stream
<< line_id
<< " | "
<< Left24 [ expr::stream << expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") ]
<< " [" << logging::trivial::severity << "]"
<< " - " << expr::smessage;

哪些打印在魔杖盒上直播

3 | 2020-08-15, 11:37:30.128 [warning] - this is a warning message
4 | 2020-08-15, 11:37:30.129 [error] - this is an error message
5 | 2020-08-15, 11:37:30.129 [fatal] - this is a fatal error message

提取值

您可能希望提取真实数据,而不是文本后处理。我发现这比我希望的要难,但无论如何都要展示,以防它对任何人有帮助:

boost::phoenix::function<milliseconds_f> milliseconds;
logging::formatter formatter = expr::format(
"%1% | %2%.%3% [ %4% ] - %5%")
% line_id
% expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S")
% milliseconds(expr::attr<boost::posix_time::ptime>("TimeStamp").or_throw())
% logging::trivial::severity
% expr::smessage;

现在,milliseconds_f定义为:

struct milliseconds_f {
auto operator()(logging::value_ref<boost::posix_time::ptime> const& v) const {
auto f = v.get().time_of_day().fractional_seconds();
std::string s = std::to_string(f / 1000);
while (s.length()<3) s += '0';
return s;
}
};

在魔杖盒上直播

3 | 2020-08-15, 12:27:38.870 [ warning ] - this is a warning message
4 | 2020-08-15, 12:27:38.870 [ error ] - this is an error message
5 | 2020-08-15, 12:27:38.870 [ fatal ] - this is a fatal error message

替代方案:更接近您的期望

你可以做一个懒惰的函数来做有效的格式化:

boost::phoenix::function<format_ex> format;
logging::formatter formatter = expr::format(
"%1% | %2%.%3% [ %4% ] - %5%")
% line_id
% expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S")
% format(std::string("%.3s"), expr::format_date_time(timestamp, "%f"))
% logging::trivial::severity
% expr::smessage;

哪里

struct format_ex {
template<typename... T>
auto operator()(std::string const& format_str, T const&... v) const {
return (boost::format(format_str) % ... % v);
}
};

由于抽象泄漏,您需要确保格式字符串不是引用的 char[] 文字。您还可以强制衰减(丑陋,但不那么冗长):

% format(+"%.3s", expr::format_date_time(timestamp, "%f"))

为了回避整个问题,可以将 Phoenix Bind 与格式化程序分开使用:

using boost::phoenix::bind;
logging::formatter formatter = expr::format(
"%1% | %2%.%3% [ %4% ] - %5%")
% line_id
% expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S")
% bind(format_f{"%.3s"}, expr::format_date_time(timestamp, "%f"))
% logging::trivial::severity
% expr::smessage;

struct format_f {
std::string format_str;

template<typename... T>
std::string operator()(T const&... v) const {
return (boost::format(format_str) % ... % v).str();
}
};

魔杖盒上查看这两个直播

混合

尝试以这种方式将expr::streamformat混合:

boost::phoenix::function<format_f> left24 = format_f{"%.24s"};
logging::formatter formatter = expr::stream
<< line_id << " | "
<< left24(expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f"))
<< " [ " << logging::trivial::severity
<< " ] - " << expr::smessage;

与上述相同的format_fLive On Wandbox,打印:

3 | 2020-08-15, 12:55:39.426 [ warning ] - this is a warning message
4 | 2020-08-15, 12:55:39.426 [ error ] - this is an error message
5 | 2020-08-15, 12:55:39.426 [ fatal ] - this is a fatal error message

或者

boost::phoenix::function<format_ex> format;
logging::formatter formatter = expr::stream
<< line_id << " | "
<< format(+"%.24s", expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f"))
<< " [ " << logging::trivial::severity
<< " ] - " << expr::smessage;

住在魔杖盒上

3 | 2020-08-15, 12:59:35.964 [ warning ] - this is a warning message
4 | 2020-08-15, 12:59:35.965 [ error ] - this is an error message
5 | 2020-08-15, 12:59:35.965 [ fatal ] - this is a fatal error message

最新更新