| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | #include "WSJTXLogging.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | #include <exception>
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | #include <sstream>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 21:38:50 +01:00
										 |  |  | #include <boost/version.hpp>
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | #include <boost/log/core.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/utility/exception_handler.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/trivial.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/sinks/text_file_backend.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/sinks/async_frontend.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/expressions.hpp>
 | 
					
						
							|  |  |  | #include <boost/log/expressions/formatters/date_time.hpp>
 | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  | #include <boost/log/expressions/predicates/channel_severity_filter.hpp>
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | #include <boost/log/support/date_time.hpp>
 | 
					
						
							|  |  |  | #include <boost/date_time/posix_time/posix_time.hpp>
 | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  | #include <boost/date_time/gregorian/greg_day.hpp>
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | #include <boost/container/flat_map.hpp>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  | #include <QtGlobal>
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | #include <QDir>
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | #include <QFile>
 | 
					
						
							|  |  |  | #include <QTextStream>
 | 
					
						
							|  |  |  | #include <QString>
 | 
					
						
							|  |  |  | #include <QStandardPaths>
 | 
					
						
							|  |  |  | #include <QRegularExpression>
 | 
					
						
							|  |  |  | #include <QMessageLogContext>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Logger.hpp"
 | 
					
						
							|  |  |  | #include "qt_helpers.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace logging = boost::log; | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  | namespace trivial = logging::trivial; | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | namespace keywords = logging::keywords; | 
					
						
							|  |  |  | namespace expr = logging::expressions; | 
					
						
							|  |  |  | namespace sinks = logging::sinks; | 
					
						
							| 
									
										
										
										
											2020-11-20 13:45:44 +00:00
										 |  |  | namespace posix_time = boost::posix_time; | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  | namespace gregorian = boost::gregorian; | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | namespace container = boost::container; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  | BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level) | 
					
						
							| 
									
										
										
										
											2020-09-27 16:52:19 +01:00
										 |  |  | BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | namespace | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |   // Top level exception handler that gets exceptions from filters and
 | 
					
						
							|  |  |  |   // formatters.
 | 
					
						
							|  |  |  |   struct exception_handler | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     typedef void result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void operator () (std::runtime_error const& e) const | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       std::cout << "std::runtime_error: " << e.what () << std::endl; | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |     void operator () (std::logic_error const& e) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       std::cout << "std::logic_error: " << e.what () << std::endl; | 
					
						
							| 
									
										
										
										
											2021-01-27 18:02:01 +00:00
										 |  |  |       throw; | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Reroute Qt messages to the system logger
 | 
					
						
							|  |  |  |   void qt_log_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Convert Qt message types to logger severities
 | 
					
						
							|  |  |  |     auto severity = trivial::trace; | 
					
						
							|  |  |  |     switch (type) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |       case QtDebugMsg: severity = trivial::debug; break; | 
					
						
							|  |  |  |       case QtInfoMsg: severity = trivial::info; break; | 
					
						
							|  |  |  |       case QtWarningMsg: severity = trivial::warning; break; | 
					
						
							|  |  |  |       case QtCriticalMsg: severity = trivial::error; break; | 
					
						
							|  |  |  |       case QtFatalMsg: severity = trivial::fatal; break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     // Map non-default Qt categories to logger channels, Qt logger
 | 
					
						
							|  |  |  |     // context is mapped to the appropriate logger attributes.
 | 
					
						
							|  |  |  |     auto log = sys::get (); | 
					
						
							|  |  |  |     std::string file; | 
					
						
							|  |  |  |     std::string function; | 
					
						
							|  |  |  |     if (context.file) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         file = context.file; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     if (context.function) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         function = context.function; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     if (!context.category || !qstrcmp (context.category, "default")) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         BOOST_LOG_SEV (log, severity) | 
					
						
							|  |  |  |           << boost::log::add_value ("Line", context.line) | 
					
						
							|  |  |  |           << boost::log::add_value ("File", file) | 
					
						
							|  |  |  |           << boost::log::add_value ("Function", function) | 
					
						
							| 
									
										
										
										
											2020-12-04 19:01:05 +00:00
										 |  |  |           << msg.toStdWString (); | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2020-12-02 00:40:37 +00:00
										 |  |  |         BOOST_LOG_SEV (log, severity) | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |           << boost::log::add_value ("Line", context.line) | 
					
						
							|  |  |  |           << boost::log::add_value ("File", file) | 
					
						
							|  |  |  |           << boost::log::add_value ("Function", function) | 
					
						
							| 
									
										
										
										
											2020-12-04 19:01:05 +00:00
										 |  |  |           << context.category << ": " << msg.toStdWString (); | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | WSJTXLogging::WSJTXLogging () | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |   auto core = logging::core::get (); | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |   // Catch relevant exceptions from logging.
 | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |   core->set_exception_handler | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |     ( | 
					
						
							|  |  |  |      logging::make_exception_handler<std::runtime_error, std::logic_error> (exception_handler {}) | 
					
						
							|  |  |  |      ); | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |   // Check for a user-defined logging configuration settings file.
 | 
					
						
							|  |  |  |   QFile log_config {QStandardPaths::locate (QStandardPaths::ConfigLocation, "wsjtx_log_config.ini")}; | 
					
						
							|  |  |  |   if (log_config.exists () && log_config.open (QFile::ReadOnly) && log_config.isReadable ()) | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |       QTextStream ts {&log_config}; | 
					
						
							|  |  |  |       auto config = ts.readAll (); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       // Substitution variables.
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |       container::flat_map<QString, QString> replacements = | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |          {"DesktopLocation", QStandardPaths::writableLocation (QStandardPaths::DesktopLocation)}, | 
					
						
							|  |  |  |          {"DocumentsLocation", QStandardPaths::writableLocation (QStandardPaths::DocumentsLocation)}, | 
					
						
							|  |  |  |          {"TempLocation", QStandardPaths::writableLocation (QStandardPaths::TempLocation)}, | 
					
						
							|  |  |  |          {"HomeLocation", QStandardPaths::writableLocation (QStandardPaths::HomeLocation)}, | 
					
						
							|  |  |  |          {"CacheLocation", QStandardPaths::writableLocation (QStandardPaths::CacheLocation)}, | 
					
						
							|  |  |  |          {"GenericCacheLocation", QStandardPaths::writableLocation (QStandardPaths::GenericCacheLocation)}, | 
					
						
							|  |  |  |          {"GenericDataLocation", QStandardPaths::writableLocation (QStandardPaths::GenericDataLocation)}, | 
					
						
							|  |  |  |          {"AppDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)}, | 
					
						
							|  |  |  |          {"AppLocalDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)}, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       // Parse the configration settings substituting the variable if found.
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |       QString new_config; | 
					
						
							|  |  |  |       int pos {0}; | 
					
						
							|  |  |  |       QRegularExpression subst_vars {R"(\${([^}]+)})"}; | 
					
						
							|  |  |  |       auto iter = subst_vars.globalMatch (config); | 
					
						
							|  |  |  |       while (iter.hasNext ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto match = iter.next (); | 
					
						
							|  |  |  |           auto const& name = match.captured (1); | 
					
						
							|  |  |  |           auto repl_iter = replacements.find (name); | 
					
						
							|  |  |  |           auto repl = repl_iter != replacements.end () ? repl_iter->second : "${" + name + "}"; | 
					
						
							|  |  |  |           new_config += config.mid (pos, match.capturedStart (1) - 2 - pos) + repl; | 
					
						
							|  |  |  |           pos = match.capturedEnd (0); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       new_config += config.mid (pos); | 
					
						
							| 
									
										
										
										
											2020-12-04 19:01:05 +00:00
										 |  |  |       std::wstringbuf buffer {new_config.toStdWString (), std::ios_base::in}; | 
					
						
							|  |  |  |       std::wistream stream {&buffer}; | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |       Logger::init_from_config (stream); | 
					
						
							| 
									
										
										
										
											2020-10-11 00:02:49 +01:00
										 |  |  |       LOG_INFO ("Read logging configuration file: " << log_config.fileName ()); | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |   else                          // Default setup
 | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       //
 | 
					
						
							|  |  |  |       // Define sinks, filters, and formatters using expression
 | 
					
						
							|  |  |  |       // templates for efficiency.
 | 
					
						
							|  |  |  |       //
 | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       // Default log file location.
 | 
					
						
							|  |  |  |       QDir app_data {QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)}; | 
					
						
							|  |  |  |       Logger::init ();          // Basic setup of attributes
 | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       //
 | 
					
						
							|  |  |  |       // Sink intended for general use that passes everything above
 | 
					
						
							|  |  |  |       // selected severity levels per channel. Log file is appended
 | 
					
						
							|  |  |  |       // between sessions and rotated to limit storage space usage.
 | 
					
						
							|  |  |  |       //
 | 
					
						
							|  |  |  |       auto sys_sink = boost::make_shared<sinks::asynchronous_sink<sinks::text_file_backend>> | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |          keywords::auto_flush = false | 
					
						
							| 
									
										
										
										
											2020-10-03 00:54:05 +01:00
										 |  |  | #if BOOST_VERSION / 100 >= 1070
 | 
					
						
							| 
									
										
										
										
											2020-12-04 19:01:05 +00:00
										 |  |  |          , keywords::file_name = app_data.absoluteFilePath ("wsjtx_syslog.log").toStdWString () | 
					
						
							| 
									
										
										
										
											2020-10-03 02:36:27 +01:00
										 |  |  |          , keywords::target_file_name = | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2020-10-03 02:36:27 +01:00
										 |  |  |          , keywords::file_name = | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-10-03 02:36:27 +01:00
										 |  |  |              app_data.absoluteFilePath ("logs/wsjtx_syslog_%Y-%m.log").toStdString () | 
					
						
							| 
									
										
										
										
											2020-10-03 00:54:05 +01:00
										 |  |  |          , keywords::time_based_rotation = sinks::file::rotation_at_time_point (gregorian::greg_day (1), 0, 0, 0) | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |          , keywords::open_mode = std::ios_base::out | std::ios_base::app | 
					
						
							| 
									
										
										
										
											2020-11-20 13:45:44 +00:00
										 |  |  | #if BOOST_VERSION / 100 >= 1063
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |          , keywords::enable_final_rotation = false | 
					
						
							| 
									
										
										
										
											2020-11-20 13:45:44 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |          ); | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       sys_sink->locked_backend ()->set_file_collector | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |          sinks::file::make_collector | 
					
						
							|  |  |  |          ( | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  |           keywords::max_size = 40 * 1024 * 1024 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |           , keywords::min_free_space = 1024 * 1024 * 1024 | 
					
						
							| 
									
										
										
										
											2020-10-02 21:32:48 +01:00
										 |  |  |           , keywords::max_files = 12 | 
					
						
							| 
									
										
										
										
											2020-12-04 19:01:05 +00:00
										 |  |  |           , keywords::target = app_data.absoluteFilePath ("logs").toStdWString () | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |           ) | 
					
						
							|  |  |  |          ); | 
					
						
							|  |  |  |       sys_sink->locked_backend ()->scan_for_files (); | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Per channel severity level filter
 | 
					
						
							| 
									
										
										
										
											2020-09-27 16:52:19 +01:00
										 |  |  |       using min_severity_filter = expr::channel_severity_filter_actor<std::string, trivial::severity_level>; | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  |       min_severity_filter min_severity = expr::channel_severity_filter (channel, severity); | 
					
						
							| 
									
										
										
										
											2020-09-27 16:52:19 +01:00
										 |  |  |       min_severity["SYSLOG"] = trivial::info; | 
					
						
							|  |  |  |       min_severity["RIGCTRL"] = trivial::info; | 
					
						
							|  |  |  |       min_severity["DATALOG"] = trivial::info; | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  |       sys_sink->set_filter (min_severity || severity >= trivial::fatal); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       sys_sink->set_formatter | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |          expr::stream | 
					
						
							| 
									
										
										
										
											2020-09-26 15:15:47 +01:00
										 |  |  |          << "[" << channel | 
					
						
							| 
									
										
										
										
											2020-11-20 13:45:44 +00:00
										 |  |  |          << "][" << expr::format_date_time<posix_time::ptime> ("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") | 
					
						
							|  |  |  |          << "][" << expr::format_date_time<posix_time::time_duration> ("Uptime", "%O:%M:%S.%f") | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  |          << "][" << trivial::severity | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |          << "] " << expr::message | 
					
						
							|  |  |  |          ); | 
					
						
							| 
									
										
										
										
											2020-09-26 14:15:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |       core->add_sink (sys_sink); | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 00:44:56 +01:00
										 |  |  |   // Indicate start of logging
 | 
					
						
							|  |  |  |   LOG_INFO ("Log Start"); | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |   ::qInstallMessageHandler (&qt_log_handler); | 
					
						
							| 
									
										
										
										
											2020-09-18 21:23:11 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | WSJTXLogging::~WSJTXLogging () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   LOG_INFO ("Log Finish"); | 
					
						
							| 
									
										
										
										
											2020-11-28 13:49:35 +00:00
										 |  |  |   auto core = logging::core::get (); | 
					
						
							|  |  |  |   core->flush (); | 
					
						
							|  |  |  |   core->remove_all_sinks (); | 
					
						
							| 
									
										
										
										
											2020-11-13 22:23:58 +00:00
										 |  |  | } |