1 #include "TraceFile.hpp"
2 
3 #include <stdexcept>
4 
5 #include <QDebug>
6 #include <QMutex>
7 #include <QMutexLocker>
8 #include <QString>
9 #include <QFile>
10 #include <QTextStream>
11 
12 #include "pimpl_impl.hpp"
13 
14 class TraceFile::impl
15 {
16 public:
17   impl (QString const& trace_file_path);
18   ~impl ();
19 
20   // no copying
21   impl (impl const&) = delete;
22   impl& operator = (impl const&) = delete;
23 
24 private:
25   // write Qt messages to the diagnostic log file
26   static void message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg);
27 
28   QFile file_;
29   QTextStream stream_;
30   QTextStream * original_stream_;
31   QtMessageHandler original_handler_;
32   static QTextStream * current_stream_;
33   static QMutex mutex_;
34 };
35 
36 QTextStream * TraceFile::impl::current_stream_;
37 QMutex TraceFile::impl::mutex_;
38 
39 // delegate to implementation class
TraceFile(QString const & trace_file_path)40 TraceFile::TraceFile (QString const& trace_file_path)
41   : m_ {trace_file_path}
42 {
43 }
44 
~TraceFile()45 TraceFile::~TraceFile ()
46 {
47 }
48 
49 
impl(QString const & trace_file_path)50 TraceFile::impl::impl (QString const& trace_file_path)
51   : file_ {trace_file_path}
52   , original_stream_ {current_stream_}
53   , original_handler_ {nullptr}
54 {
55   // if the log file is writeable; initialise diagnostic logging to it
56   // for append and hook up the Qt global message handler
57   if (file_.open (QFile::WriteOnly | QFile::Append | QFile::Text))
58     {
59       stream_.setDevice (&file_);
60       current_stream_ = &stream_;
61       original_handler_ = qInstallMessageHandler (message_handler);
62     }
63 }
64 
~impl()65 TraceFile::impl::~impl ()
66 {
67   // unhook our message handler before the stream and file are destroyed
68   if (original_handler_)
69     {
70       qInstallMessageHandler (original_handler_);
71     }
72   current_stream_ = original_stream_; // revert to prior stream
73 }
74 
75 // write Qt messages to the diagnostic log file
message_handler(QtMsgType type,QMessageLogContext const & context,QString const & msg)76 void TraceFile::impl::message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
77 {
78   Q_ASSERT_X (current_stream_, "TraceFile:message_handler", "no stream to write to");
79   {
80     QMutexLocker lock {&mutex_}; // thread safety - serialize writes to the trace file
81     *current_stream_ << qFormatLogMessage (type, context, msg) <<
82 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
83                  endl;
84 #else
85                  Qt::endl;
86 #endif
87   }
88 
89   if (QtFatalMsg == type)
90     {
91       throw std::runtime_error {"Fatal Qt Error"};
92     }
93 }
94