5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "appdebugmessagehandler.h"
23 #include <QFileDevice>
25 AppDebugMessageHandler::AppDebugMessageHandler(QObject
* parent
) :
27 m_appDebugOutputLevel(APP_DBG_HANDLER_DEFAULT_LEVEL
),
28 m_showSourcePath(APP_DBG_HANDLER_SHOW_SRC_PATH
),
29 m_showFunctionDeclarations(APP_DBG_HANDLER_SHOW_FUNCTION_DECL
)
31 m_srcPathFilter
= QRegularExpression("^" APP_DBG_HANDLER_SRC_PATH
"[\\\\\\/](.*?)$", QRegularExpression::InvertedGreedinessOption
);
32 m_functionFilter
= QRegularExpression("^.+?(\\w+\\().+$");
35 AppDebugMessageHandler
* AppDebugMessageHandler::instance()
37 static AppDebugMessageHandler
* _instance
= NULL
;
38 #if APP_DBG_HANDLER_ENABLE
39 if(_instance
== NULL
&& !qApp
->closingDown())
40 _instance
= new AppDebugMessageHandler(qApp
); // Ensure this object is cleaned up when the QApplication exits.
45 void AppDebugMessageHandler::setAppDebugOutputLevel(const quint8
& appDebugOutputLevel
)
47 m_appDebugOutputLevel
= appDebugOutputLevel
;
50 void AppDebugMessageHandler::setShowSourcePath(bool showSourcePath
)
52 m_showSourcePath
= showSourcePath
;
55 void AppDebugMessageHandler::setShowFunctionDeclarations(bool showFunctionDeclarations
)
57 m_showFunctionDeclarations
= showFunctionDeclarations
;
60 void AppDebugMessageHandler::addOutputDevice(QIODevice
* device
)
62 if (device
&& !m_outputDevices
.contains(device
))
63 m_outputDevices
.append(device
);
66 void AppDebugMessageHandler::removeOutputDevice(QIODevice
* device
)
69 // no QVector::removeAll() in Qt < 5.4
71 foreach (QIODevice
* d
, m_outputDevices
) {
73 m_outputDevices
.remove(i
);
79 void AppDebugMessageHandler::installAppMessageHandler()
81 m_defaultHandler
= qInstallMessageHandler(g_appDebugMessageHandler
);
84 void AppDebugMessageHandler::messageHandler(QtMsgType type
, const QMessageLogContext
& context
, const QString
& msg
)
86 static const char symbols
[] = { 'D', 'W', 'E', 'X', 'I' }; // correspond to QtMsgType enum
87 // normalize types, QtDebugMsg stays 0, QtInfoMsg becomes 1, rest are QtMsgType + 1
89 if (type
== QtInfoMsg
)
91 else if (type
> QtDebugMsg
)
94 if (lvl
< m_appDebugOutputLevel
&& type
!= QtFatalMsg
)
97 #if defined(Q_OS_LINUX) && (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
98 // Filter out lots of QPainter warnings from undocked QDockWidgets... hackish but effective (only workaround found so far)
99 if (lvl
== 2 && QString(context
.function
).contains("QPainter::"))
103 QString msgPattern
= QString("[%1] ").arg(symbols
[type
]);
105 if (m_showSourcePath
) {
106 QString file
= QString("%1::").arg(context
.file
);
107 file
.replace(m_srcPathFilter
, "\\1");
108 msgPattern
.append(file
);
110 if (m_showFunctionDeclarations
)
111 msgPattern
.append(context
.function
);
113 msgPattern
.append("%{function}()");
115 msgPattern
.append(":%{line} -%{if-category} [%{category}]%{endif} %{message}");
117 if (type
== QtFatalMsg
)
118 msgPattern
.append("\nBACKTRACE:\n%{backtrace depth=12 separator=\"\n\"}");
120 qSetMessagePattern(msgPattern
);
122 if (!m_defaultHandler
|| m_outputDevices
.size() || receivers(SIGNAL(messageOutput(quint8
, const QString
&)))) {
123 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
124 msgPattern
= qFormatLogMessage(type
, context
, msg
);
126 msgPattern
.replace("%{line}", QString::number(context
.line
));
127 msgPattern
.replace("%{if-category} [%{category}]%{endif}", QString(context
.category
));
128 msgPattern
.replace("%{message}", msg
);
129 if (!m_showFunctionDeclarations
) {
130 QString func
= context
.function
;
131 msgPattern
.replace("%{function}", func
.replace(m_functionFilter
, "\\1)"));
135 emit
messageOutput(lvl
, msgPattern
);
137 foreach (QIODevice
* d
, m_outputDevices
) {
138 if (d
&& d
->isWritable() && (!d
->property("level").isValid() || d
->property("level").toInt() <= lvl
)) {
139 d
->write(qPrintable(msgPattern
% "\n"));
140 if (QFileDevice
* fd
= qobject_cast
<QFileDevice
*>(d
))
146 // if (QThread::currentThread() == qApp->thread()) // gui thread
148 if (m_defaultHandler
) {
149 m_defaultHandler(type
, context
, msg
);
152 fprintf(stderr
, "%s", qPrintable(msgPattern
));
153 if (type
== QtFatalMsg
)
158 // Message handler which is installed using qInstallMessageHandler. This needs to be global.
159 void g_appDebugMessageHandler(QtMsgType type
, const QMessageLogContext
&context
, const QString
&msg
)
161 if (AppDebugMessageHandler::instance())
162 AppDebugMessageHandler::instance()->messageHandler(type
, context
, msg
);