I am following the tutorial on Boost.log record formatting and I'm trying to format a timestamp to only show 2 digits on the fractional seconds part. The same question was asked 9 years ago here how to customize "TimeStamp" format of Boost.Log, but the solution doesn't seem to work with more recent versions of Boost. Here's what I tried which is similar to said solution, but with format_date_time< boost::posix_time::ptime >
instead of 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);
What seems to be the relevant error is:
/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;
Is there a way to convert the object returned by expr::format()
to something that can be injected into the expr::stream
?
UPDATE
I have found a solution, which is to use a custom formatter which takes a formatting_ostream
and a record_view
, and extract the timestamp from the record. Then I do the formatting using a local_date_time
object and output_facet
s, and finally write it back to the ostream using boost::format
on the fractional seconds string. It is pretty ugly; There must be a better way.
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);
...
}
UPDATE 2
Just in case someone stumbles upon this and wants to use @sehe's format_f
with a C++11 compiler:
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...);
}
};