diff --git a/CMakeLists.txt b/CMakeLists.txt index fcd5a8a2e..84bcc6d77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -248,7 +248,6 @@ set (wsjt_qt_CXXSRCS item_delegates/ForeignKeyDelegate.cpp validators/LiveFrequencyValidator.cpp GetUserId.cpp - TraceFile.cpp Audio/AudioDevice.cpp Transceiver/Transceiver.cpp Transceiver/TransceiverBase.cpp @@ -753,7 +752,6 @@ set (all_C_and_CXXSRCS ) set (TOP_LEVEL_RESOURCES - wsjtx_log_config.ini icons/Darwin/wsjtx.iconset/icon_128x128.png contrib/gpl-v3-logo.svg artwork/splash.png @@ -885,7 +883,8 @@ find_program(ETAGS etags) # set (BOOST_ROOT ${PROJECT_SOURCE_DIR}/boost) # endif () set (Boost_USE_STATIC_LIBS OFF) -find_package (Boost 1.63 REQUIRED COMPONENTS log_setup log) +find_package (Boost 1.63 REQUIRED COMPONENTS log_setup) +add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED) # # OpenMP @@ -1432,9 +1431,9 @@ set_target_properties (wsjtx PROPERTIES target_include_directories (wsjtx PRIVATE ${FFTW3_INCLUDE_DIRS}) if (APPLE) - target_link_libraries (wsjtx Qt5::SerialPort wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES}) + target_link_libraries (wsjtx wsjt_fort) else () - target_link_libraries (wsjtx Qt5::SerialPort wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES}) + target_link_libraries (wsjtx wsjt_fort_omp) if (OpenMP_C_FLAGS) set_target_properties (wsjtx PROPERTIES COMPILE_FLAGS "${OpenMP_C_FLAGS}" @@ -1450,6 +1449,7 @@ else () ) endif () endif () +target_link_libraries (wsjtx Qt5::SerialPort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES}) # make a library for WSJT-X UDP servers # add_library (wsjtx_udp SHARED ${UDP_library_CXXSRCS}) diff --git a/WSJTXLogging.cpp b/WSJTXLogging.cpp index f8fecdbbf..c2bbda00a 100644 --- a/WSJTXLogging.cpp +++ b/WSJTXLogging.cpp @@ -1,9 +1,21 @@ #include "WSJTXLogging.hpp" +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -15,21 +27,48 @@ #include "qt_helpers.hpp" namespace logging = boost::log; +namespace keywords = logging::keywords; +namespace expr = logging::expressions; +namespace sinks = logging::sinks; +namespace ptime = boost::posix_time; namespace container = boost::container; +namespace +{ + // 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 + { + std::cout << "std::runtime_error: " << e.what () << std::endl; + } + void operator () (std::logic_error const& e) const + { + std::cout << "std::logic_error: " << e.what () << std::endl; + //throw; + } + }; +} + WSJTXLogging::WSJTXLogging () { + // Catch relevant exceptions from logging. + logging::core::get ()->set_exception_handler + ( + logging::make_exception_handler (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.setFileName (":/wsjtx_log_config.ini"); - } - if (log_config.open (QFile::ReadOnly) && log_config.isReadable ()) + if (log_config.exists () && log_config.open (QFile::ReadOnly) && log_config.isReadable ()) { QTextStream ts {&log_config}; auto config = ts.readAll (); - // substitute variable + // Substitution variables. container::flat_map replacements = { {"DesktopLocation", QStandardPaths::writableLocation (QStandardPaths::DesktopLocation)}, @@ -42,6 +81,7 @@ WSJTXLogging::WSJTXLogging () {"AppDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)}, {"AppLocalDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)}, }; + // Parse the configration settings substituting the variable if found. QString new_config; int pos {0}; QRegularExpression subst_vars {R"(\${([^}]+)})"}; @@ -59,11 +99,62 @@ WSJTXLogging::WSJTXLogging () std::stringbuf buffer {new_config.toStdString (), std::ios_base::in}; std::istream stream {&buffer}; Logger::init_from_config (stream); + LOG_INFO ("Unable to read logging configuration file: " << log_config.fileName ()); } - else + else // Default setup { - LOG_WARN ("Unable to read logging configuration file: " << log_config.fileName ()); + // + // Define sinks, filters, and formatters using expression + // templates for efficiency. + // + // Default log file location. + QDir app_data {QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)}; + Logger::init (); // Basic setup of attributes + auto core = logging::core::get (); + // + // 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> + ( + keywords::auto_flush = false + , keywords::file_name = app_data.absoluteFilePath ("wsjtx_syslog.log").toStdString () + , keywords::target_file_name = app_data.absoluteFilePath ("old_logs/wsjtx_syslog_%Y-%m-%d_%H-%M-%S.%N.log").toStdString () + , keywords::rotation_size = 5 * 1024 * 1024 + , keywords::time_based_rotation = sinks::file::rotation_at_time_point (0, 0, 0) + , keywords::open_mode = std::ios_base::out | std::ios_base::app + , keywords::enable_final_rotation = false + ); + sys_sink->locked_backend ()->set_file_collector + ( + sinks::file::make_collector + ( + keywords::target = app_data.absoluteFilePath ("old_logs").toStdString () + , keywords::max_size = 40 * 1024 * 1024 + , keywords::min_free_space = 1024 * 1024 * 1024 + , keywords::max_files = 10 + ) + ); + sys_sink->locked_backend ()->scan_for_files (); + sys_sink->set_formatter + ( + expr::stream + << "[" << expr::format_date_time ("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") + << "][" << expr::format_date_time ("Uptime", "%O:%M:%S.%f") + << "][" << logging::trivial::severity + << "] " << expr::message + ); + core->add_sink (sys_sink); + +#if !defined (NDEBUG) && defined (Q_OS_WIN) + // auto windbg_sink = boost::make_shared> (); + // windbg_sink->set_filter (logging::trivial::severity >= logging::trivial::trace && expr::is_debugger_present ()); + // logging::core::get ()->add_sink (windbg_sink); +#endif } + // Indicate start of logging + LOG_INFO ("Log Start"); } WSJTXLogging::~WSJTXLogging () diff --git a/wsjtx_log_config.ini b/wsjtx_log_config.ini index 0a508d63e..b90f6cd10 100644 --- a/wsjtx_log_config.ini +++ b/wsjtx_log_config.ini @@ -17,12 +17,10 @@ Destination="TextFile" Asynchronous="true" # If AutoFlush is true then non-buffered output AutoFlush="true" -Append="true" # Line Formats available: TimeStamp, Uptime, Severity, LineID (counter), ProcessID, ThreadID, Line, File, Function # TimeStamp and Uptime support boost date time format: # http://www.boost.org/doc/libs/1_60_0/doc/html/date_time/date_time_io.html#date_time.format_flags -#Format="[%TimeStamp(format=\"%Y-%m-%d %H:%M:%S.%f\")%][%Uptime(format=\"%O:%M:%S.%f\")%][%Severity%] %File%(%Line%) %Function%: %Message%" -Format="[%TimeStamp(format=\"%Y-%m-%d %H:%M:%S.%f\")%][%Severity%\t] %Message%" +Format="[%TimeStamp(format=\"%Y-%m-%d %H:%M:%S.%f\")%][%Uptime(format=\"%O:%M:%S.%f\")%][%Severity%] %File%(%Line%) %Function%: %Message%" # Target directory in which rotated files will be stored. Target="${AppLocalDataLocation}/old_logs" # FileName pattern to use. %N is a counter for files. @@ -31,14 +29,17 @@ TargetFileName="wsjtx_syslog_%5N.log" # RotationSize in bytes, File size, in bytes, upon which file rotation will be performed. # Time based rotation also available via RotationInterval and RotationTimePoint see boost log docs. RotationSize="1048576" -EnableFinalRotation="false" +# If Append is true then do not start a new log file for each session +Append="false" +# EnableFinalRotation will rotate on close, make this false if using Append true +EnableFinalRotation="true" # Matching used so that only files matching FileName pattern are deleted. ScanForFiles="Matching" # MaxSize - Total size of files in the target directory in bytes upon which the oldest file will be deleted. #MaxSize=100485760 # MinFreeSpace - Minimum free space in the Target directory in bytes. Above this value oldest file is deleted. #MinFreeSpace=1485760 -MaxFiles="10" +#MaxFiles="10" # Specify level of log, options are: trace, debug, info, warning, error, fatal # Since Channel not part of filter all log output will be included. # If only SYSLOG logging desired, change to: Filter="%Severity% >= trace & %Channel% matches \"SYSLOG\"" @@ -50,7 +51,7 @@ Destination="TextFile" # If Asynchronous true then thread dedicated to writing to log, otherwise blocks main thread to write. Asynchronous="true" # If AutoFlush is true then non-buffered output -AutoFlush="true" +AutoFlush="false" Append="true" # Line Formats available: TimeStamp, Uptime, Severity, LineID (counter), ProcessID, ThreadID # TimeStamp and Uptime support boost date time format: