24#include <boost/core/null_deleter.hpp>
25#include <boost/date_time/posix_time/posix_time.hpp>
26#include <boost/filesystem.hpp>
27#include <boost/log/expressions.hpp>
28#include <boost/log/expressions/formatters/date_time.hpp>
29#include <boost/log/utility/setup/file.hpp>
30#include <boost/log/utility/setup/common_attributes.hpp>
31#include <boost/log/support/date_time.hpp>
32#include <boost/log/sources/severity_feature.hpp>
33#include <boost/phoenix/bind/bind_function.hpp>
37#include <ql/errors.hpp>
39using namespace boost::filesystem;
40using namespace boost::posix_time;
43namespace logging = boost::log;
44namespace lexpr = boost::log::expressions;
45namespace lsinks = boost::log::sinks;
46namespace lsrc = boost::log::sources;
47namespace lkeywords = boost::log::keywords;
48namespace lattr = boost::log::attributes;
76 QL_REQUIRE(!
buffer_.empty(),
"Log Buffer is empty");
85 fout_.open(filename.c_str(), ios_base::out);
86 QL_REQUIRE(
fout_.is_open(),
"Error opening file " << filename);
87 fout_.setf(ios::fixed, ios::floatfield);
88 fout_.setf(ios::showpoint);
105 QuantLib::ext::shared_ptr<lsinks::text_ostream_backend> backend = QuantLib::ext::make_shared<lsinks::text_ostream_backend>();
108 QuantLib::ext::shared_ptr<text_sink> sink(
new text_sink(backend));
114 auto formatter = [
this](
const logging::record_view& rec, logging::formatting_ostream& strm) {
115 this->
messages().push_back(rec[lexpr::smessage]->c_str());
120 lsrc::severity_logger_mt<oreSeverity> lg;
121 lg.add_attribute(messageType.get_name(), lattr::constant<string>(
name));
122 BOOST_LOG_SEV(lg, oreSeverity::notice) << rec[lexpr::smessage];
128 sink->set_formatter(formatter);
129 logging::core::get()->add_sink(sink);
135 logging::core::get()->remove_sink(
fileSink_);
139 logging::core::get()->remove_sink(
coutSink_);
145 if (rotationSize == 0) {
147 lkeywords::target = dir,
148 lkeywords::file_name = filepath,
150 lkeywords::auto_flush =
true
154 lkeywords::target = dir,
155 lkeywords::file_name = filepath,
157 lkeywords::scan_method = lsinks::file::scan_matching,
158 lkeywords::rotation_size = rotationSize,
159 lkeywords::auto_flush =
true
168 QuantLib::ext::shared_ptr<lsinks::text_ostream_backend> backend = QuantLib::ext::make_shared<lsinks::text_ostream_backend>();
169 backend->add_stream(QuantLib::ext::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
172 QuantLib::ext::shared_ptr<text_sink> sink(
new text_sink(backend));
174 logging::core::get()->add_sink(sink);
180 logging::core::get()->remove_sink(
coutSink_);
187 QuantLib::ext::shared_ptr<lsinks::text_ostream_backend> backend = QuantLib::ext::make_shared<lsinks::text_ostream_backend>();
190 QuantLib::ext::shared_ptr<text_sink> sink(
new text_sink(backend));
194 auto formatter = [
this](
const logging::record_view& rec, logging::formatting_ostream& strm) {
195 const string& msg = rec[lexpr::smessage]->c_str();
203 oreSeverity logSeverity = boost::log::extract<oreSeverity>(severity.get_name(), rec).get();
207 lsrc::severity_logger_mt<oreSeverity> lg;
208 lg.add_attribute(messageType.get_name(), lattr::constant<string>(
name));
209 BOOST_LOG_SEV(lg, logSeverity) << rec[lexpr::smessage];
216 sink->set_formatter(formatter);
217 logging::core::get()->add_sink(sink);
223 logging::core::get()->remove_sink(
fileSink_);
229 if (rotationSize == 0) {
231 lkeywords::target = dir,
232 lkeywords::file_name = filepath,
234 lkeywords::auto_flush =
true
238 lkeywords::target = dir,
239 lkeywords::file_name = filepath,
241 lkeywords::scan_method = lsinks::file::scan_matching,
242 lkeywords::rotation_size = rotationSize,
243 lkeywords::auto_flush =
true
250 logging::core::get()->remove_sink(
fileSink_);
257 lkeywords::file_name = filepath +
"%Y-%m-%d" +
".json",
258 lkeywords::time_based_rotation = lsinks::file::rotation_at_time_point(0, 0, 0),
260 lkeywords::scan_method = lsinks::file::scan_matching,
261 lkeywords::auto_flush =
true
266 const std::function<
void(
const boost::log::record_view&, boost::log::formatting_ostream&)>& formatter) {
272Log::Log() : loggers_(), enabled_(false), mask_(255), ls_() {
274 ls_.setf(ios::fixed, ios::floatfield);
275 ls_.setf(ios::showpoint);
279 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
281 "Logger with name " <<
logger->name() <<
" already registered");
286 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
288 "Logger with name " <<
logger->name() <<
" already registered as independent logger");
293 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
299 boost::shared_lock<boost::shared_mutex> lock(
mutex_);
304 boost::shared_lock<boost::shared_mutex> lock(
mutex_);
310 boost::shared_lock<boost::shared_mutex> lock(
mutex_);
315 boost::shared_lock<boost::shared_mutex> lock(
mutex_);
321 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
322 map<string, QuantLib::ext::shared_ptr<Logger>>::iterator it =
loggers_.find(
name);
326 QL_FAIL(
"No logger found with name " <<
name);
331 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
334 it->second->removeSinks();
337 QL_FAIL(
"No independdent logger found with name " <<
name);
342 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
344 logging::core::get()->remove_all_sinks();
353 filepath = relative(path(filename),
rootPath_).string();
355 int lineNoLen = (int)log10((
double)lineNo) + 1;
356 int len = 2 + filepath.length() + 1 + lineNoLen + 1;
359 return "(" + filepath +
':' +
to_string(lineNo) +
')';
363 return string(
maxLen_ - len,
' ') +
"(" + filepath +
':' +
to_string(lineNo) +
')';
367 return "(..." + filepath.substr(3 + len -
maxLen_) +
':' +
to_string(lineNo) +
')';
373 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
378 boost::unique_lock<boost::shared_mutex> lock(
mutex_);
383 boost::shared_lock<boost::shared_mutex> lock(
mutex_);
428 ls_ <<
'[' << to_simple_string(microsec_clock::local_time()) <<
']';
432 ls_ <<
" " <<
source(filename, lineNo) <<
" : ";
450 string msg =
ls_.str();
451 map<string, QuantLib::ext::shared_ptr<Logger>>::iterator it;
454 l.second->log(m, msg);
459 suffix =
" ... suppressing more messages from same source code location (cutoff = " +
463 l.second->log(m, msg + suffix);
472 : mask_(mask), filename_(filename), lineNo_(lineNo), ss_() {
475 "Invalid log mask " << mask);
480 while (getline(
ss_, text)) {
482 if (ore::data::Log::instance().enabled() && ore::data::Log::instance().
filter(
mask_)) {
483 boost::unique_lock<boost::shared_mutex> lock(ore::data::Log::instance().mutex());
485 ore::data::Log::instance().logStream() << text;
486 ore::data::Log::instance().log(
mask_);
492 if (!ore::data::Log::instance().checkExcludeFilters(
msg()))
497 if (
obj.type() ==
typeid(map<string, boost::any>)) {
498 string jsonStr =
"{ ";
500 for (
const auto& kv : boost::any_cast<map<string, boost::any>>(
obj)) {
503 jsonStr +=
'\"' + kv.first +
"\": " +
jsonify(kv.second);
508 }
else if (
obj.type() ==
typeid(vector<boost::any>)) {
509 string arrayStr =
"[ ";
511 for (
const auto& v : boost::any_cast<vector<boost::any>>(
obj)) {
519 }
else if (
obj.type() ==
typeid(
string)) {
520 string str = boost::any_cast<string>(
obj);
521 boost::replace_all(str,
"\\",
"\\\\");
522 boost::replace_all(str,
"\"",
"\\\"");
523 boost::replace_all(str,
"\r",
"\\r");
524 boost::replace_all(str,
"\n",
"\\n");
525 return '\"' + str +
'\"';
527 return to_string(boost::any_cast<StructuredMessage::Category>(
obj));
529 return to_string(boost::any_cast<StructuredMessage::Group>(
obj));
530 }
else if (
obj.type() ==
typeid(
int)) {
532 }
else if (
obj.type() ==
typeid(
bool)) {
534 }
else if (
obj.type() ==
typeid(QuantLib::Size)) {
536 }
else if (
obj.type() ==
typeid(QuantLib::Real)) {
538 }
else if (
obj.type() ==
typeid(
unsigned int)) {
540 }
else if (
obj.type() ==
typeid(
unsigned short)) {
542 }
else if (
obj.type() ==
typeid(
float)) {
556 const map<string, string>& subFields) {
559 data_[
"message"] = message;
561 if (!subFields.empty()) {
562 vector<boost::any> subFieldsVector;
563 bool addedSubField =
false;
564 for (
const auto& sf : subFields) {
565 if (!sf.second.empty()) {
566 map<string, boost::any> subField({{
"name", sf.first}, {
"value", sf.second}});
567 subFieldsVector.push_back(subField);
568 addedSubField =
true;
572 data_[
"sub_fields"] = subFieldsVector;
577 lsrc::severity_logger_mt<oreSeverity> lg;
578 lg.add_attribute(messageType.get_name(), lattr::constant<string>(
name));
580 auto it =
data_.find(
"category");
581 QL_REQUIRE(it !=
data_.end(),
"StructuredMessage must have a 'category' key specified.");
582 QL_REQUIRE(it->second.type() ==
typeid(
string),
"StructuredMessage category must be a string.");
584 string category = boost::any_cast<string>(it->second);
586 BOOST_LOG_SEV(lg, oreSeverity::warning) <<
json();
588 BOOST_LOG_SEV(lg, oreSeverity::alert) <<
json();
590 QL_FAIL(
"StructuredMessage::log() invalid category '" << category <<
"'");
595 if (!subFields.empty()) {
598 bool hasNonEmptySubField =
false;
599 for (
const auto& sf : subFields) {
600 if (!sf.second.empty()) {
601 hasNonEmptySubField =
true;
605 if (!hasNonEmptySubField)
608 if (
data_.find(
"sub_fields") ==
data_.end()) {
609 data_[
"sub_fields"] = vector<boost::any>();
612 for (
const auto& sf : subFields) {
613 if (!sf.second.empty()) {
614 map<string, boost::any> subField({{
"name", sf.first}, {
"value", sf.second}});
615 boost::any_cast<vector<boost::any>&>(
data_.at(
"sub_fields")).push_back(subField);
623 lsrc::severity_logger_mt<oreSeverity> lg;
624 lg.add_attribute(messageType.get_name(), lattr::constant<string>(
name));
625 BOOST_LOG_SEV(lg, oreSeverity::alert) <<
json();
626 MLOG(oreSeverity::alert,
msg());
632 data_[
"detail"] = detail;
633 data_[
"progress"] = progressCurrent;
634 data_[
"total"] = progressTotal;
635 data_[
"@timestamp"] =
636 boost::posix_time::to_iso_extended_string(boost::posix_time::microsec_clock::universal_time());
640 lsrc::severity_logger_mt<oreSeverity> lg;
641 lg.add_attribute(messageType.get_name(), lattr::constant<string>(
name));
642 BOOST_LOG_SEV(lg, oreSeverity::notice) <<
json();
651 out <<
"UnknownType";
653 QL_FAIL(
"operator<<: Unsupported enum value for StructuredMessage::Category");
662 out <<
"Configuration";
674 out <<
"Reference Data";
676 out <<
"UnknownType";
678 QL_FAIL(
"operator<<: Unsupported enum value for StructuredMessage::Group");
static const std::string name
the name "BufferLogger"
std::queue< std::string > buffer_
virtual void log(unsigned, const std::string &) override
The log callback.
bool hasNext()
Checks if Logger has new messages.
std::string next()
Retrieve new messages.
static const std::string name
the name "EventLogger"
virtual void removeSinks() override
Destructor.
QuantLib::ext::shared_ptr< file_sink > fileSink_
void setFileLog(const std::string &filepath)
void setFormatter(const std::function< void(const boost::log::record_view &, boost::log::formatting_ostream &)> &)
void emitLog() const
generate Boost log record to pass to corresponding sinks
std::string msg() const
return a std::string for the log file
static constexpr const char * name
virtual ~FileLogger()
Destructor.
static const std::string name
the name "FileLogger"
virtual void log(unsigned, const std::string &) override
The log callback.
FileLogger(const std::string &filename)
Constructor.
Base Log handler class that utilises Boost logging to create log sinks.
std::vector< std::string > & messages()
std::vector< std::string > messages_
const std::string json() const
create JSON-like output from the data
static std::string jsonify(const boost::any &)
virtual std::string msg() const =0
return a std::string for the log file
std::map< std::string, boost::any > data_
void log() const
generate Boost log record to pass to corresponding sinks
virtual void emitLog() const =0
generate Boost log record - this method is called by log()
void removeLogger(const std::string &name)
Remove a Logger.
std::map< std::string, QuantLib::ext::shared_ptr< Logger > > loggers_
QuantLib::ext::shared_ptr< Logger > & logger(const std::string &name)
Retrieve a Logger.
std::map< std::string, std::function< bool(const std::string &)> > excludeFilters_
const bool hasIndependentLogger(const std::string &name) const
std::size_t sameSourceLocationCutoff_
std::size_t sameSourceLocationSince_
boost::filesystem::path rootPath_
const bool hasLogger(const std::string &name) const
Check if logger exists.
void header(unsigned m, const char *filename, int lineNo)
macro utility function - do not use directly, not thread safe
std::string source(const char *filename, int lineNo) const
void removeIndependentLogger(const std::string &name)
void registerIndependentLogger(const QuantLib::ext::shared_ptr< IndependentLogger > &logger)
boost::shared_mutex mutex_
std::map< std::string, QuantLib::ext::shared_ptr< IndependentLogger > > independentLoggers_
void removeAllLoggers()
Remove all loggers.
bool checkExcludeFilters(const std::string &)
QuantLib::ext::shared_ptr< IndependentLogger > & independentLogger(const std::string &name)
void addExcludeFilter(const std::string &, const std::function< bool(const std::string &)>)
std::string lastFileName_
void removeExcludeFilter(const std::string &)
void registerLogger(const QuantLib::ext::shared_ptr< Logger > &logger)
Add a new Logger.
void clearAllIndependentLoggers()
bool writeSuppressedMessagesHint_
void log(unsigned m)
macro utility function - do not use directly, not thread safe
The Base Custom Log Handler class.
LoggerStream(unsigned mask, const char *filename, unsigned lineNo)
~LoggerStream()
destructor - this is when the logging takes place.
static const std::string name
the name "ProgressLogger"
virtual void removeSinks() override
Destructor.
ProgressLogger()
Constructors.
QuantLib::ext::shared_ptr< text_sink > coutSink_
QuantLib::ext::shared_ptr< file_sink > fileSink_
const QuantLib::ext::shared_ptr< file_sink > & fileSink()
void setCoutLog(bool flag)
void setFileLog(const std::string &filepath, const boost::filesystem::path &dir, QuantLib::Size rotationSize=0)
QuantLib::ext::shared_ptr< text_sink > cacheSink_
ProgressMessage(const std::string &, const QuantLib::Size, const QuantLib::Size, const std::string &detail="")
void emitLog() const
generate Boost log record to pass to corresponding sinks
static constexpr const char * name
static const std::string name
the name "StderrLogger"
static const std::string name
the name "StructuredLogger"
virtual void removeSinks() override
Destructor.
QuantLib::ext::shared_ptr< file_sink > fileSink_
const QuantLib::ext::shared_ptr< file_sink > & fileSink()
StructuredLogger()
Constructors.
void setFileLog(const std::string &filepath, const boost::filesystem::path &dir, QuantLib::Size rotationSize=0)
QuantLib::ext::shared_ptr< text_sink > cacheSink_
void addSubFields(const std::map< std::string, std::string > &)
void emitLog() const
generate Boost log record to pass to corresponding sinks
static constexpr const char * name
StructuredMessage(const Category &category, const Group &group, const std::string &message, const std::map< std::string, std::string > &subFields=std::map< std::string, std::string >())
SafeStack< Filter > filter
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", oreSeverity)
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > text_sink
std::string to_string(const LocationInfo &l)
Serializable Credit Default Swap.
string conversion utilities