js8call/JS8_Main/TraceFile.cpp

113 lines
3 KiB
C++
Raw Permalink Normal View History

2026-01-18 12:53:55 -06:00
/**
* @file TraceFile.cpp
* @brief Implementation of TraceFile class for logging
*/
2026-01-18 12:53:55 -06:00
#include "TraceFile.h"
#include "JS8_Include/pimpl_impl.h"
#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>
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"};
}
}