Boost use monotonic clock as log timestamp - c++

I use this function to init logging with boost and it works well
void initBoostLog(std::string logfile)
{
boost::log::add_file_log
(
boost::log::keywords::file_name = logfile,
boost::log::keywords::auto_flush = true,
boost::log::keywords::format =
(
boost::log::expressions::stream
<< "[" << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "] <" << boost::log::trivial::severity
<< "> " << boost::log::expressions::smessage
)
);
boost::log::add_common_attributes();
boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::info);
}
It gives log like this as output
[2020-07-29 16:07:20.726137] <info> My log starts
[2020-07-29 16:07:20.726619] <info> Next log line
But I want the clock to be the monotonic clock similar to this from standard C (what I rightly or wrongly call the system uptime)
struct timespec monotime;
clock_gettime(CLOCK_MONOTONIC, &monotime);
return (float)(monotime.tv_sec + monotime.tv_nsec/1.0e9);
So that log would log something like this
[1130.726137] <info> My log starts
[1130.726619] <info> Next log line
Boost offers a monotonic clock
boost::log::detail::get_timestamp()
But I don't know now how to use it in the init of the boost log?
How can it be used or can be solved in some other way?
My system is Ubuntu 16.04 arm64
Update after #sehe answer
sehes' answer works like clockwork (pun intended!) the only thing I had to add was to set the precision in the stream. Otherwise I would only get 2 or 3 decimals (same code different Ubuntu 16.04 installations) like this
boost::log::expressions::stream
<< "[" << std::fixed << std::setprecision(6) << boost::log::expressions::attr<double>("monoclock")
<< "] <" << boost::log::trivial::severity
<< "> " << boost::log::expressions::smessage

Boost offers a monotonic clock boost::log::detail::get_timestamp()
It's a bit misleading to say it "offers" that. It's expressly in a detail namespace so you cannot rely on it.
Is it monotonic?
It turns out that on POSIX the implementation is switched anyways:
# if defined(_POSIX_MONOTONIC_CLOCK)
//! \c get_timestamp implementation based on POSIX monotonic clock
timestamp get_timestamp_monotonic_clock() {
// ...
}
# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_monotonic_clock
# else // if defined(_POSIX_MONOTONIC_CLOCK)
# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_realtime_clock
# endif // if defined(_POSIX_MONOTONIC_CLOCK)
The switch only depends on _POSIX_MONOTONIC_CLOCK which is defined in unistd.h https://linux.die.net/man/3/clock_gettime
Availability
On POSIX systems on which these functions are available, the symbol _POSIX_TIMERS is defined in <unistd.h> to a value greater than 0. The symbols _POSIX_MONOTONIC_CLOCK, _POSIX_CPUTIME, _POSIX_THREAD_CPUTIME indicate that CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID are available. (See also sysconf(3).)
So, yes it does use monotonic clock if available. (It does fall back to realtime if monotonic fails at runtime, which makes sense with ported binaries).
Is it used?
Yes. It's used in queued sinks, to dequeue new messages as per a "ordering window": That's not your format attribute, though.
How to make it work
Define your own attribute! We can use an adhoc formatting expression:
auto adhoc =
expr::wrap_formatter([](boost::log::record_view const&, boost::log::formatting_ostream& strm) {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
strm << std::fixed << std::setprecision(6) << (monotime.tv_sec + monotime.tv_nsec/1.0e9);
});
When used:
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << adhoc << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
We get:
[768386.969232] <info> An informational severity message
[768386.969656] <warning> A warning severity message
[768386.969793] <error> An error severity message
[768386.969887] <fatal> A fatal severity message
See it Live On Coliru
Improvement suggestions
I called that ad-hoc, because actually it doesn't log the timestamp of the meesage, but rather the timestamp of formatting, which might significantly differ. In that respect, you may want to actually define a custom attribute which will be stored in the record, so you can format that instead.
Custom Attribute: monoclock
It turned out to be pretty simple: function objects as attributes.
static inline double get_monoclock() {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
return monotime.tv_sec + monotime.tv_nsec/1.0e9;
}
All that's required to add the attribte to all records:
boost::log::core::get()->add_global_attribute("monoclock", attrs::make_function(&get_monoclock));
You're then free to use it in your formatters:
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << expr::attr<double>("monoclock") << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
Again, Live On Coliru
[504214.461181] <info> An informational severity message
[504214.462371] <warning> A warning severity message
[504214.462405] <error> An error severity message
[504214.462433] <fatal> A fatal severity message
Full Listing
Again, Live On Coliru
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
static inline double get_monoclock() {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
return monotime.tv_sec + monotime.tv_nsec/1.0e9;
}
void initBoostLog(std::string logfile) {
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
boost::log::add_file_log(
boost::log::keywords::file_name = logfile,
boost::log::keywords::auto_flush = true,
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << std::fixed << expr::attr<double>("monoclock") << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
boost::log::add_common_attributes();
boost::log::core::get()->add_global_attribute("monoclock", attrs::make_function(&get_monoclock));
boost::log::core::get()->set_filter(boost::log::trivial::severity >=
boost::log::trivial::info);
}
int main() {
initBoostLog("test.log");
using namespace boost::log::trivial;
boost::log::sources::severity_logger< severity_level > lg;
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
BOOST_LOG_SEV(lg, info) << "An informational severity message";
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
BOOST_LOG_SEV(lg, error) << "An error severity message";
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
}

Related

Boost Log V2: setup log filter from configuration file

I want to setup only one Boost Log V2 sink (Boost 1.75.0) to filter out few lines from the log records producing the application.
The goal would be to have a log file containing only these lines (e.g.: users who logged in), while another sink logs out every records
I have tried to configure the filter property of the sink in the configuration file to match to a tag presence, like this:
[Sinks.FileSink]
Destination=TextFile
Filter="%USER%"
FileName="log.log"
MaxSize=10000000
Format="[%TimeStamp%] - %Message%"
Asynchronous=false
AutoFlush=true
Where the %USER% tag would be the filter criteria, if it is present, log the line, otherwise not...
I tried to add as value attribute:
... << boost::log::add_value("USER", true) << "message..."
and also register it before logger from file:
boost::log::core::get()->add_global_attribute("USER", boost::log::attributes::mutable_constant(true));
boost::log::init_from_stream(config);
None of them work
For me, the strange behavioral was that filter pattern works if I filter for severity, like this: Filter="%Severity%"
Can anybody help me how to filter only really few lines from application and forward them into file?
Thank you
Update:
a minimal example that wants to demonstrator what I want to achieve:
#include <boost/log/utility/setup/from_stream.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
int main(int argc, char *argv_char[]) {
std::stringstream config;
config << R"([Core])" << std::endl;
config << R"(DisableLogging=false)" << std::endl;
config << R"(Filter="%Severity% >= trace")" << std::endl;
config << R"([Sinks.Sink])" << std::endl;
config << R"(Destination=Console)" << std::endl;
//config << R"(Filter="%CMD% = \"YES\"")" << std::endl;
config << R"(Filter="%CMD% = \"NO\"")" << std::endl;
config << R"(FileName="commands.log")" << std::endl;
config << R"(Format="%Message%")" << std::endl;
config << R"(Asynchronous=false)" << std::endl;
config << R"(AutoFlush=true)" << std::endl;
boost::log::core::get()->add_global_attribute("CMD", boost::log::attributes::mutable_constant<std::string>("NO"));
boost::log::init_from_stream(config);
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Visible message";
return 0;
}
If I replace the filter condition to %CMD% = "YES", the desired log line is missing on the output
Update 2:
// Codes above didn't changed
boost::log::init_from_stream(config);
boost::log::sources::logger logger;
logger.add_attribute("CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(logger) << "A log message from logger";
boost::log::sources::logger loggerScoped;
BOOST_LOG_SCOPED_LOGGER_ATTR(loggerScoped, "CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(loggerScoped) << "A log message from loggerScoped";
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Visible message";
return 0;
As stated in the documentation, add_value manipulator has no effect on filtering as it is being executed after filtering is done. In your code sample, you're setting a global string attribute "CMD" with a value of "NO", and that is what is being tested by the filters for both log records. When you change the filter to require the value of "YES", both log records get suppressed.
If you want to selectively disable some log records, you should use other means for registering the attribute, so that it is in effect at the point of filtering. For example, you could add the attribute to a logger (by calling add_attribute on the logger) and use that logger to emit log records you want to suppress. Or you could use scoped attributes to mark log records that are being emitted in a given region of code. Below is an updated code example:
#include <string>
#include <sstream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/from_stream.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/sources/logger.hpp>
int main(int argc, char *argv_char[]) {
std::stringstream config;
config << R"([Core])" << std::endl;
config << R"(DisableLogging=false)" << std::endl;
//config << R"(Filter="%Severity% >= trace")" << std::endl;
config << R"([Sinks.Sink])" << std::endl;
config << R"(Destination=Console)" << std::endl;
config << R"(Filter="%CMD% = \"YES\"")" << std::endl;
config << R"(Format="%Message%")" << std::endl;
config << R"(Asynchronous=false)" << std::endl;
config << R"(AutoFlush=true)" << std::endl;
boost::log::core::get()->add_global_attribute("CMD", boost::log::attributes::mutable_constant<std::string>("NO"));
boost::log::init_from_stream(config);
boost::log::sources::logger logger;
logger.add_attribute("CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(logger) << "A log message from logger";
{
boost::log::sources::logger loggerScoped;
BOOST_LOG_SCOPED_LOGGER_ATTR(loggerScoped, "CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(loggerScoped) << "A log message from loggerScoped";
}
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Still non-visible message";
return 0;
}

Passing boost log message as an argument to function call

I have written C++ code for capturing the various severity levels of messages. I have used https://github.com/gklingler/simpleLogger for this.
File simpleLogger.cpp
#include "simpleLogger.h"
#include <boost/log/core/core.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <ostream>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
src::severity_logger_mt<boost::log::trivial::severity_level> logger;
// add attributes
logger.add_attribute("LineID", attrs::counter<unsigned int>(1)); // lines are sequentially numbered
logger.add_attribute("TimeStamp", attrs::local_clock()); // each log line gets a timestamp
// add a text sink
typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
// add a logfile stream to our sink
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(LOGFILE));
// add "console" output stream to our sink
sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// specify the format of the log message
logging::formatter formatter = expr::stream
<< std::setw(7) << std::setfill('0') << line_id << std::setfill(' ') << " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << expr::smessage;
sink->set_formatter(formatter);
// only messages with severity >= SEVERITY_THRESHOLD are written
sink->set_filter(severity >= SEVERITY_THRESHOLD);
// "register" our sink
logging::core::get()->add_sink(sink);
return logger;
}
File simpleLogger.h
#ifndef simpleLogger_h__
#define simpleLogger_h__
#define BOOST_LOG_DYN_LINK // necessary when linking the boost_log library dynamically
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
// the logs are also written to LOGFILE
#define LOGFILE "logfile.log"
// just log messages with severity >= SEVERITY_THRESHOLD are written
#define SEVERITY_THRESHOLD logging::trivial::warning
// register a global logger
BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>)
// just a helper macro used by the macros below - don't use it in your code
#define LOG(severity) BOOST_LOG_SEV(logger::get(),boost::log::trivial::severity)
// ===== log macros =====
#define LOG_TRACE LOG(trace)
#define LOG_DEBUG LOG(debug)
#define LOG_INFO LOG(info)
#define LOG_WARNING LOG(warning)
#define LOG_ERROR LOG(error)
#define LOG_FATAL LOG(fatal)
#endif
File app.cpp
#include "simpleLogger.h"
int main() {
LOG_TRACE << "this is a trace message";
LOG_DEBUG << "this is a debug message";
LOG_WARNING << "this is a warning message";
LOG_ERROR << "this is an error message";
LOG_FATAL << "this is a fatal error message";
return 0;
}
So I would send my message as
LOG_INFO << "This is info message"
But this log message I need to send it to some other function as an argument. In that function I will be doing some other changes on the log message i.e "This is info message".
How to send the boost log message as an argument to function?
I didn't find relevant source for this.
Thanks in advance
You can define a "lazy actor" that you can put into a wrapped formatter expression. This is pretty much the rocket science of Boost Log, but perhaps this simple example will help you:
auto reverse_expr = [](auto fmt) {
return expr::wrap_formatter([fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
std::reverse(text.begin(), text.end());
strm << text;
});
};
// specify the format of the log message
logging::formatter formatter = expr::stream
<< reverse_expr(expr::stream << std::setw(7) << std::setfill('0') << line_id)
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< reverse_expr(expr::stream << expr::smessage);
sink->set_formatter(formatter);
Results in:
3000000 | 2020-04-28, 16:15:15.779204 [warning] - egassem gninraw a si siht
4000000 | 2020-04-28, 16:15:15.779308 [error] - egassem rorre na si siht
5000000 | 2020-04-28, 16:15:15.779324 [fatal] - egassem rorre lataf a si siht
Notes: it will not be overly efficient because it involves "double buffering" with a temporary stream, but it is highly flexible, as you can see.
UPDATE Improved method below (see section BONUS)
A slightly more generic implementation might look like:
template <typename F> struct Xfrm {
Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f=_f,fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
So you can actually use it for other functions:
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) ch = std::toupper(ch);
return v;
}
Which you could then use like:
logging::formatter formatter = expr::stream
<< Xfrm(reversed)[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< Xfrm(to_uppercase)[ expr::stream << expr::smessage ];
Or put the manipulators in your header file:
static inline constexpr Xfrm UC{to_uppercase};
static inline constexpr Xfrm REV{reversed};
So you can use it pre-fab:
logging::formatter formatter = expr::stream
<< REV[ expr::stream << std::setw(7) << std::setfill('0') << line_id ]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< UC[ expr::stream << expr::smessage ];
Output
3000000 | 2020-04-28, 16:36:46.184958 [warning] - THIS IS A WARNING MESSAGE
4000000 | 2020-04-28, 16:36:46.185034 [error] - THIS IS AN ERROR MESSAGE
5000000 | 2020-04-28, 16:36:46.185045 [fatal] - THIS IS A FATAL ERROR MESSAGE
Live Demo
Live On Wandbox
File simpleLogger.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#pragma once
#define BOOST_LOG_DYN_LINK \
1 // necessary when linking the boost_log library dynamically
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/trivial.hpp>
// the logs are also written to LOGFILE
#define LOGFILE "logfile.log"
// just log messages with severity >= SEVERITY_THRESHOLD are written
#define SEVERITY_THRESHOLD logging::trivial::warning
// register a global logger
BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level>)
// just a helper macro used by the macros below - don't use it in your code
#define LOG(severity) \
BOOST_LOG_SEV(logger::get(), boost::log::trivial::severity)
// ===== log macros =====
#define LOG_TRACE LOG(trace)
#define LOG_DEBUG LOG(debug)
#define LOG_INFO LOG(info)
#define LOG_WARNING LOG(warning)
#define LOG_ERROR LOG(error)
#define LOG_FATAL LOG(fatal)
#endif
File simpleLogger.cpp
#include "simpleLogger.h"
#include <boost/core/null_deleter.hpp>
#include <boost/log/core/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/char_decorator.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/make_shared.hpp>
#include <boost/phoenix.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <ostream>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity",
logging::trivial::severity_level)
namespace /*extend locally*/ {
template <typename F> struct Xfrm {
constexpr Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f = _f, fmt](logging::record_view const& rec,
logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) {
ch = std::toupper(ch);
}
return v;
}
inline constexpr Xfrm UC{ to_uppercase };
inline constexpr Xfrm REV{ reversed };
} // namespace
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
src::severity_logger_mt<boost::log::trivial::severity_level> logger;
// add attributes
logger.add_attribute("LineID", attrs::counter<unsigned int>(
1)); // lines are sequentially numbered
logger.add_attribute(
"TimeStamp", attrs::local_clock()); // each log line gets a timestamp
// add a text sink
using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
// add a logfile stream to our sink
sink->locked_backend()->add_stream(
boost::make_shared<std::ofstream>(LOGFILE));
// add "console" output stream to our sink
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// specify the format of the log message
logging::formatter formatter =
expr::stream
<< REV[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << UC[expr::stream << expr::smessage];
sink->set_formatter(formatter);
// only messages with severity >= SEVERITY_THRESHOLD are written
sink->set_filter(severity >= SEVERITY_THRESHOLD);
// "register" our sink
logging::core::get()->add_sink(sink);
return logger;
}
File test.cpp
#include "simpleLogger.h"
int main() {
LOG_TRACE << "this is a trace message";
LOG_DEBUG << "this is a debug message";
LOG_WARNING << "this is a warning message";
LOG_ERROR << "this is an error message";
LOG_FATAL << "this is a fatal error message";
return 0;
}
BONUS: More Efficient
To avoid double-buffering, we can avoid using the original formatter. This works best for known attributes:
struct OddEvenId {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto vr = line_id.or_throw()(rec);
if (!vr.empty()) {
strm << std::setw(4) << (vr.get<uint32_t>()%2? "Odd":"Even");
}
}
};
struct QuotedMessage {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto vr = expr::smessage.or_throw()(rec);
if (!vr.empty())
strm << std::quoted(vr.get<std::string>());
}
};
static inline auto oddeven_id = expr::wrap_formatter(OddEvenId{});
static inline auto qmessage = expr::wrap_formatter(QuotedMessage{});
Now we can simply say qmessage instead of expr::smessage to get the quoted message value:
Live On Wandbox
logging::formatter formatter = expr::stream
<< oddeven_id
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << qmessage;
Prints
Odd | 2020-04-28, 17:21:12.619565 [warning] - "this is a warning message"
Even | 2020-04-28, 17:21:12.619666 [error] - "this is an error message"
Odd | 2020-04-28, 17:21:12.619684 [fatal] - "this is a fatal error message"

C++ date library fails with timezone

This used to play once. I am trying to get some data from the C++ date library but an exception is caught. I am compiling with
-DUSE_AUTOLOAD=0 -DHAS_REMOTE_API=0 -DUSE_OS_TZDB=1
what is wrong with the code?
#include <iostream>
#include "date/tz.h"
#include <exception>
using namespace date;
using namespace std::chrono;
int main(int argc, char** argv) {
try {
auto current_time_zone = make_zoned("Europe/Athens", std::chrono::system_clock::now());
auto current_day = date::format("%A", current_time_zone);
auto current_time = date::format("%H:%M", current_time_zone);
std::cout << "day: " << current_day << ", time: " << current_time << " in timezone: " << current_time_zone << std::endl;
//std::cout << " in timezone: " << current_time_zone << std::endl;
} catch ( std::exception& e) {
std::cout << e.what() << std::endl;
}
}
You need to use the -pthread flag. tz.cpp uses call_once to do part of the initialisation. And without -pthread it's not going to work (as underneath it need something like __gthread_once). See this for more details.
You can verify if that's the problem by running your example with gdb (use the catch throw).
I'm not positive what the problem is, but I can tell you that this library does not throw an exception that contains the message "Unknown error".
Try adding -DONLY_C_LOCALE=1 to your build flags. This will avoid your std::lib's time_put facet, but will limit you to only the "C" locale. If this fixes the problem, then it is your std::lib's std::time_put facet that threw the exception.

Crash in boost::coroutine library when used alongside boost::property_tree XML parser

I'm using Simple-Web-Server library for creating simple web service for translation of XML to JSON and vice versa. On its turn it uses several boost libraries as well boost::coroutine among them. For XML<->JSON conversion I'm using boost::property_tree library for intermediate representation. Here is the code:
#include <iostream>
#include <sstream>
#include <server_http.hpp>
#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
using namespace std;
using namespace boost::property_tree;
using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;
int main()
{
HttpServer server(8080, 1);
server.resource["^/json_to_xml$"]["POST"] = [](auto& response, auto request) {
try
{
ptree pt;
read_json(request->content, pt);
ostringstream json, xml;
write_json(json, pt);
clog << "JSON request content:" << endl << json.str() << endl;
write_xml(xml, pt, xml_writer_make_settings<ptree::key_type>(' ', 1u));
clog << "XML response content:" << endl << xml.str() << endl;
response << "HTTP/1.1 200 OK\r\nContent-Length: " << xml.str().length() << "\r\n\r\n" << xml.str();
}
catch(const exception& e)
{
cerr << "Error:" << endl << e.what() << endl;
response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what();
}
};
server.resource["^/xml_to_json$"]["POST"] = [](auto& response, auto request) {
try
{
ptree pt;
read_xml(request->content, pt, xml_parser::trim_whitespace | xml_parser::no_comments);
ostringstream xml, json;
write_xml(xml, pt, xml_writer_make_settings<ptree::key_type>(' ', 1u));
clog << "XML request content:" << endl << xml.str() << endl;
write_json(json, pt);
clog << "JSON response content: " << endl << json.str() << endl;
response << "HTTP/1.1 200 OK\r\nContent-Length: " << json.str().length() << "\r\n\r\n" << json.str();
}
catch(const exception& e)
{
cerr << "Error:" << endl << e.what() << endl;
response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n" << e.what();
}
};
server.start();
return 0;
}
JSON to XML conversion works fine but the opposite causes the program to crash. When I'm not using boost::property_tree's XML parser in the server's callback the program works fine. Here is the result of execution. Here is GDB backtrace. And finally here is Valgrind output.
Used version of the boost libraries is 1.58.0 but the same result is observed with the newest version 1.61.0. Version 1.4.2 of the Simple-Web-Server is used.
read_xml() might be overflowing your coroutine's stack; see here about a similar crash traced down to a 64K stack variable inside the rapidxml parser.
EDIT. To summarize the link... the gist of the matter is, the rapidxml parser buried within read_xml allocates 64K on the stack, which overflows the 8K default coroutine stack on Linux. You can try two things... either force that stack variable to be smaller, e.g.,
#define BOOST_PROPERTY_TREE_RAPIDXML_STATIC_POOL_SIZE 512
#include <boost/property_tree/xml_parser.hpp>
or allocate a larger stack when the coroutine is spawned (if it's possible to access that through Simple-Web-Server)

How can I write some trace in my log file in C++

I want to write the date of the execution and the end of execution of a file in my log file.
I can't install anything, just use standard module ( I execute my code in command line with linux ).
I want something like this :
[TRACE] 2014-07-24 14:18:50,2014-07-24 14:18:52
I have this result for the moment :
[TRACE] , Start date of execution : Aug 25 2014 : 10:43:02
End date of execution : Mon Aug 25 10:43:06 2014
here my code :
#include <iostream>
#include <string>
#include <fstream>
#include <ctime>
using namespace std;
void startDateExecution(fstream& file) {
if(fichier)
{
file << "[TRACE]" << " , " << "Start date of execution : " << __DATE__ << " : " << __TIME__ << endl;
}
else
cerr << "Unable to open file" << endl;
}
void endDateExecution(fstream& file) {
time_t result = time(NULL);
file << "End date of execution : " << asctime(localtime(&result)) << endl;
file.close();
}
void displayDate(fstream& file) {
startDateExecution(file);
endDateExecution(file);
}
int main(){
fstream file("trace.log", ios::out | ios::trunc);
displayDate(file);
return 0;
}
You can use log4cpp library. It has lots of other features too. There are sample programs available on the following website.
http://log4cpp.sourceforge.net/
You just need to instantiate the appender based on the needs. I have used RollingFileAppender in my project where I needed the log file to be divided after some threshold (i.e. file size reaches 1MB). Then you need to set the pattern in which you want the logs to be written.
Hope this helps.
As many have commented, __DATE__ and __TIME__ refer to the time of compilation, not execution.
You'll need to retrieve the current time both at the start and at the end of the execution; you'll use the same method, whichever one you use, in both cases.
Here's an example of how you can format time using strftime.
std::string format(time_t when)
{
char timestr[256] = {0};
const char* my_format = "%m/%d/%y # %H:%M:%S";
std::strftime(timestr, sizeof(timestr), my_format, std::localtime(&when));
return timestr;
}
You would use it like this:
int main()
{
time_t start = std::time(NULL);
// Do stuff
time_t end = std::time(NULL);
std::cout << "Start: " << format(start) << std::endl
<< "End: " << format(end) << std::endl;
}
Read the documentation for strftime to learn how to specify your own format.