Set up for Boost logging in WSJT-X

uses a  settings file to  define log  sink back-ends, by  default uses
:/wsjtx_log_config.ini  from  the  resources file-system.   Users  may
override  by placing  their own  wsjtx_log_config.ini into  the WSJT-X
config location. The settings file format is as described in the Boost
log
documentation (https://www.boost.org/doc/libs/1_74_0/libs/log/doc/html/log/detailed/utilities.html#log.detailed.utilities.setup.settings_file)
with the additional feature that  allows some pre-defined variables to
be expanded. The  predefined variables refer to  standard locations in
the file-system,  and allow  log files  and rotation  target directory
paths to be specified. The pre-defined variables are:

   DesktopLocation
   DocumentsLocation
   TempLocation
   HomeLocation
   CacheLocation
   GenericCacheLocation
   GenericDataLocation
   AppDataLocation

and  must  be   used  enclosed  on  braces  and  preceded   by  a  '$'
character. E.g. to define the pattern for a sink's log file:

   FileName="${AppLocalDataLocation}/wsjtx_syslog.log"

this would place the log file wsjtx_syslog.log in the WSJT-X log files
directory, on all platforms.
This commit is contained in:
Bill Somerville 2020-09-18 21:23:11 +01:00
parent b420c9f6d3
commit f66788691d
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
4 changed files with 152 additions and 19 deletions

View File

@ -305,6 +305,7 @@ set (jt9_FSRCS
)
set (wsjtx_CXXSRCS
WSJTXLogging.cpp
logbook/logbook.cpp
Network/PSKReporter.cpp
Modulator/Modulator.cpp

114
WSJTXLogging.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "WSJTXLogging.hpp"
#include <sstream>
#include <boost/container/flat_map.hpp>
#include <QFile>
#include <QTextStream>
#include <QString>
#include <QStandardPaths>
#include <QRegularExpression>
#include <QMessageLogContext>
#include "Logger.hpp"
#include "qt_helpers.hpp"
namespace logging = boost::log;
namespace container = boost::container;
WSJTXLogging::WSJTXLogging ()
{
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 ())
{
QTextStream ts {&log_config};
auto config = ts.readAll ();
// substitute variable
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)},
};
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);
std::stringbuf buffer {new_config.toStdString (), std::ios_base::in};
std::istream stream {&buffer};
Logger::init_from_config (stream);
}
else
{
LOG_WARN ("Unable to read logging configuration file: " << log_config.fileName ());
}
}
WSJTXLogging::~WSJTXLogging ()
{
LOG_INFO ("Log Finish");
auto lg = logging::core::get ();
lg->flush ();
lg->remove_all_sinks ();
}
// Reroute Qt messages to the system logger
void WSJTXLogging::qt_log_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
{
// Convert Qt message types to logger severities
auto severity = boost::log::trivial::trace;
switch (type)
{
case QtDebugMsg: severity = boost::log::trivial::debug; break;
case QtInfoMsg: severity = boost::log::trivial::info; break;
case QtWarningMsg: severity = boost::log::trivial::warning; break;
case QtCriticalMsg: severity = boost::log::trivial::error; break;
case QtFatalMsg: severity = boost::log::trivial::fatal; break;
}
// Map non-default Qt categories to logger channels, Qt logger
// context is mapped to the appropriate logger attributes.
auto log = Logger::sys::get ();
if (!qstrcmp (context.category, "default"))
{
BOOST_LOG_SEV (log, severity)
<< boost::log::add_value ("Line", context.line)
<< boost::log::add_value ("File", context.file)
<< boost::log::add_value ("Function", context.function)
<< msg.toStdString ();
}
else
{
BOOST_LOG_CHANNEL_SEV (log, context.category, severity)
<< boost::log::add_value ("Line", context.line)
<< boost::log::add_value ("File", context.file)
<< boost::log::add_value ("Function", context.function)
<< msg.toStdString ();
}
if (QtFatalMsg == type)
{
// bail out
throw std::runtime_error {"Fatal Qt Error"};
}
}

23
WSJTXLogging.hpp Normal file
View File

@ -0,0 +1,23 @@
#ifndef WSJTX_LOGGING_HPP__
#define WSJTX_LOGGING_HPP__
#include <QtGlobal>
class QString;
//
// Class WSJTXLogging - wraps application specific logging
//
class WSJTXLogging final
{
public:
explicit WSJTXLogging ();
~WSJTXLogging ();
//
// Install this as the Qt message handler (qInstallMessageHandler)
// to integrate Qt messages.
static void qt_log_handler (QtMsgType type, QMessageLogContext const& context, QString const&);
};
#endif

View File

@ -34,7 +34,8 @@
#include "qt_helpers.hpp"
#include "L10nLoader.hpp"
#include "SettingsGroup.hpp"
#include "TraceFile.hpp"
//#include "TraceFile.hpp"
#include "WSJTXLogging.hpp"
#include "MultiSettings.hpp"
#include "widgets/mainwindow.h"
#include "commons.h"
@ -98,9 +99,7 @@ namespace
int main(int argc, char *argv[])
{
// ### Add timestamps to all debug messages
// qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
::qInstallMessageHandler (&WSJTXLogging::qt_log_handler);
init_random_seed ();
// make the Qt type magic happen
@ -113,15 +112,15 @@ int main(int argc, char *argv[])
ExceptionCatchingApplication a(argc, argv);
try
{
// qDebug () << "+++++++++++++++++++++++++++ Resources ++++++++++++++++++++++++++++";
// LOG_INfO ("+++++++++++++++++++++++++++ Resources ++++++++++++++++++++++++++++");
// {
// QDirIterator resources_iter {":/", QDirIterator::Subdirectories};
// while (resources_iter.hasNext ())
// {
// qDebug () << resources_iter.next ();
// LOG_INFO (resources_iter.next ());
// }
// }
// qDebug () << "--------------------------- Resources ----------------------------";
// LOG_INFO ("--------------------------- Resources ----------------------------");
QLocale locale; // get the current system locale
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
@ -179,9 +178,6 @@ int main(int argc, char *argv[])
}
}
// load UI translations
L10nLoader l10n {&a, locale, parser.value (lang_option)};
QStandardPaths::setTestModeEnabled (parser.isSet (test_option));
// support for multiple instances running from a single installation
@ -224,8 +220,8 @@ int main(int argc, char *argv[])
if (QLockFile::LockFailedError == instance_lock.error ())
{
auto button = MessageBox::query_message (nullptr
, a.translate ("main", "Another instance may be running")
, a.translate ("main", "try to remove stale lock file?")
, "Another instance may be running"
, "try to remove stale lock file?"
, QString {}
, MessageBox::Yes | MessageBox::Retry | MessageBox::No
, MessageBox::Yes);
@ -244,12 +240,11 @@ int main(int argc, char *argv[])
}
}
#if WSJT_QDEBUG_TO_FILE
// Open a trace file
TraceFile trace_file {temp_dir.absoluteFilePath (a.applicationName () + "_trace.log")};
qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}");
qDebug () << program_title (revision ()) + " - Program startup";
#endif
WSJTXLogging lg;
LOG_INFO (program_title (revision ()) << " - Program startup");
// load UI translations
L10nLoader l10n {&a, locale, parser.value (lang_option)};
// Create a unique writeable temporary directory in a suitable location
bool temp_ok {false};
@ -382,7 +377,7 @@ int main(int argc, char *argv[])
a.translate ("main", "Unable to create shared memory segment"));
throw std::runtime_error {"Shared memory error"};
}
qDebug () << "shmem size:" << mem_jt9.size ();
LOG_INFO ("shmem size:" << mem_jt9.size ());
}
else
{