Boost log set up for WSJT-X

Rotated  log files  limited to  ten  saved logs  with timestamped  and
counted file names. Logs written to writeable files directory. Formats
and file  names hard coded  with expression templates  fro efficiency,
but with the ability to provide a user-defined configuration file that
overrides the hard-coded  defaults. The configuration file  must be in
the configuration files directory and named wsjtx_log_config.ini.
This commit is contained in:
Bill Somerville
2020-09-25 00:44:56 +01:00
parent 98997fb745
commit 3fafbdaaa3
3 changed files with 111 additions and 19 deletions
+99 -8
View File
@@ -1,9 +1,21 @@
#include "WSJTXLogging.hpp"
#include <exception>
#include <sstream>
#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/sinks/debug_output_backend.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/container/flat_map.hpp>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QString>
@@ -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<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.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<QString, QString> 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<sinks::asynchronous_sink<sinks::text_file_backend>>
(
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<ptime::ptime> ("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "][" << expr::format_date_time<ptime::time_duration> ("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<sinks::synchronous_sink<sinks::debug_output_backend>> ();
// 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 ()