2026-01-18 12:53:55 -06:00
|
|
|
/**
|
|
|
|
|
* @file TraceFile.cpp
|
|
|
|
|
* @brief Implementation of TraceFile class for logging
|
|
|
|
|
*/
|
2026-01-01 15:12:24 -06:00
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include "TraceFile.h"
|
|
|
|
|
#include "JS8_Include/pimpl_impl.h"
|
2026-01-01 15:12:24 -06:00
|
|
|
|
|
|
|
|
#include <QDateTime>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QMessageLogContext>
|
|
|
|
|
#include <QMutex>
|
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
#include <QString>
|
|
|
|
|
#include <QTextStream>
|
|
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include <stdexcept>
|
2026-01-01 15:12:24 -06:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
QMutex lock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class TraceFile::impl {
|
|
|
|
|
public:
|
|
|
|
|
impl(QString const &trace_file_path);
|
|
|
|
|
~impl();
|
|
|
|
|
|
|
|
|
|
// no copying
|
|
|
|
|
impl(impl const &) = delete;
|
|
|
|
|
impl &operator=(impl const &) = delete;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// write Qt messages to the diagnostic log file
|
|
|
|
|
static void message_handler(QtMsgType type,
|
|
|
|
|
QMessageLogContext const &context,
|
|
|
|
|
QString const &msg);
|
|
|
|
|
|
|
|
|
|
QFile file_;
|
|
|
|
|
QTextStream stream_;
|
|
|
|
|
QTextStream *original_stream_;
|
|
|
|
|
QtMessageHandler original_handler_;
|
|
|
|
|
static QTextStream *current_stream_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QTextStream *TraceFile::impl::current_stream_;
|
|
|
|
|
|
|
|
|
|
// delegate to implementation class
|
|
|
|
|
TraceFile::TraceFile(QString const &trace_file_path) : m_{trace_file_path} {}
|
|
|
|
|
|
|
|
|
|
TraceFile::~TraceFile() {}
|
|
|
|
|
|
|
|
|
|
TraceFile::impl::impl(QString const &trace_file_path)
|
|
|
|
|
: file_{trace_file_path}, original_stream_{current_stream_},
|
|
|
|
|
original_handler_{nullptr} {
|
|
|
|
|
// if the log file is writeable; initialise diagnostic logging to it
|
|
|
|
|
// for append and hook up the Qt global message handler
|
|
|
|
|
if (file_.open(QFile::WriteOnly | QFile::Append | QFile::Text)) {
|
|
|
|
|
stream_.setDevice(&file_);
|
|
|
|
|
current_stream_ = &stream_;
|
|
|
|
|
original_handler_ = qInstallMessageHandler(message_handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TraceFile::impl::~impl() {
|
|
|
|
|
// unhook our message handler before the stream and file are destroyed
|
|
|
|
|
if (original_handler_) {
|
|
|
|
|
qInstallMessageHandler(original_handler_);
|
|
|
|
|
}
|
|
|
|
|
current_stream_ = original_stream_; // revert to prior stream
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// write Qt messages to the diagnostic log file
|
|
|
|
|
void TraceFile::impl::message_handler(QtMsgType type,
|
|
|
|
|
QMessageLogContext const &context,
|
|
|
|
|
QString const &msg) {
|
|
|
|
|
char const *severity;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case QtDebugMsg:
|
|
|
|
|
severity = "Debug";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case QtWarningMsg:
|
|
|
|
|
severity = "Warning";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case QtFatalMsg:
|
|
|
|
|
severity = "Fatal";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
severity = "Critical";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// guard against multiple threads with overlapping messages
|
|
|
|
|
QMutexLocker guard(&lock);
|
|
|
|
|
Q_ASSERT_X(current_stream_, "TraceFile:message_handler",
|
|
|
|
|
"no stream to write to");
|
|
|
|
|
*current_stream_
|
|
|
|
|
<< QDateTime::currentDateTimeUtc().toString(
|
|
|
|
|
"yyyy-MM-ddTHH:mm:ss.zzzZ")
|
|
|
|
|
<< '(' << context.file << ':'
|
|
|
|
|
<< context.line /* << ", " << context.function */ << ')' << severity
|
|
|
|
|
<< ": " << msg.trimmed() << Qt::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (QtFatalMsg == type) {
|
|
|
|
|
throw std::runtime_error{"Fatal Qt Error"};
|
|
|
|
|
}
|
|
|
|
|
}
|