13 #ifdef SWIFT_USE_CRASHPAD
14 # include "crashpad/client/simulate_crash.h"
19 #include <QAbstractNativeEventFilter>
20 #include <QCoreApplication>
21 #include <QGlobalStatic>
22 #include <QMessageLogContext>
23 #include <QMetaMethod>
34 Q_GLOBAL_STATIC(CLogHandler, g_handler)
38 Q_ASSERT(!g_handler.isDestroyed());
43 void messageHandler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
50 QMetaObject::invokeMethod(
51 CLogHandler::instance(), [&] { messageHandler(type, context, message); }, Qt::BlockingQueuedConnection);
54 #if defined(Q_CC_MSVC) && defined(QT_NO_DEBUG)
55 if (type == QtFatalMsg)
57 struct EventFilter :
public QAbstractNativeEventFilter
60 virtual bool nativeEventFilter(
const QByteArray &,
void *message, qintptr *result)
override
62 auto msg =
static_cast<MSG *
>(message);
63 *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
68 qApp->installNativeEventFilter(&ef);
69 MessageBoxW(
nullptr, message.toStdWString().c_str(),
nullptr,
71 qApp->removeNativeEventFilter(&ef);
72 # if defined(SWIFT_USE_CRASHPAD)
73 CRASHPAD_SIMULATE_CRASH();
77 QMetaObject::invokeMethod(
CLogHandler::instance(), [statusMessage = CStatusMessage(type, context, message)] {
84 if (skipIfAlreadyInstalled && m_oldHandler) {
return; }
85 Q_ASSERT_X(!m_oldHandler, Q_FUNC_INFO,
"Re-installing the log handler should be avoided");
86 m_oldHandler = qInstallMessageHandler(messageHandler);
89 CLogHandler::CLogHandler()
92 moveToThread(QCoreApplication::instance()->thread());
95 CLogHandler::~CLogHandler() { qInstallMessageHandler(m_oldHandler); }
99 Q_ASSERT(thread() == QThread::currentThread());
101 auto finder = [&](
const PatternPair &pair) {
return pair.first == pattern; };
102 auto comparator = [](
const PatternPair &a,
const PatternPair &b) {
return a.first.isProperSubsetOf(b.first); };
104 auto it = std::find_if(m_patternHandlers.cbegin(), m_patternHandlers.cend(), finder);
105 if (it == m_patternHandlers.cend())
107 auto *handler =
new CLogPatternHandler(
this, pattern);
111 else {
return (*it).second; }
119 QList<CLogPatternHandler *> CLogHandler::handlersForMessage(
const CStatusMessage &message)
const
121 QList<CLogPatternHandler *> m_handlers;
122 for (
const auto &pair : m_patternHandlers)
124 if (pair.first.match(message)) { m_handlers.push_back(pair.second); }
129 bool CLogHandler::isFallThroughEnabled(
const QList<CLogPatternHandler *> &handlers)
const
131 for (
const auto *handler : handlers)
133 if (!handler->m_inheritFallThrough) {
return handler->m_enableFallThrough; }
135 return m_enableFallThrough;
140 using namespace swift::config;
141 CStatusMessage statusMessage = i_statusMessage;
142 if (!CBuildConfig::isLocalDeveloperDebugBuild() &&
149 if (!CBuildConfig::isLocalDeveloperDebugBuild() &&
155 if (statusMessage.getMessage().startsWith(QStringLiteral(
"libpng warning")))
161 else if (statusMessage.getMessage().startsWith(QStringLiteral(
"QCommandLineParser: option not defined")))
167 auto bucket = m_tokenBuckets.find(statusMessage);
168 if (bucket == m_tokenBuckets.end()) { bucket = m_tokenBuckets.insert(statusMessage, { { 5, 1000, 1 }, 0 }); }
169 if (!bucket->first.tryConsume())
174 if (bucket->second > 0)
176 auto copy = statusMessage;
177 copy.appendMessage(QStringLiteral(
" (+%1 identical messages)").arg(bucket->second));
185 logMessage(statusMessage);
191 logMessage(statusMessage);
197 Q_ASSERT_X(m_oldHandler, Q_FUNC_INFO,
"Install the log handler before using it");
198 Q_ASSERT_X(thread() == QThread::currentThread(), Q_FUNC_INFO,
"Wrong thread");
199 m_enableFallThrough = enable;
202 void CLogHandler::logMessage(
const CStatusMessage &statusMessage)
204 const auto handlers = handlersForMessage(statusMessage);
206 if (isFallThroughEnabled(handlers))
208 Q_ASSERT_X(m_oldHandler, Q_FUNC_INFO,
"Handler must be installed");
212 statusMessage.toQtLogTriple(&type, &category, &message);
213 m_oldHandler(type, QMessageLogContext(
nullptr, 0,
nullptr, qPrintable(category)), message);
216 for (
auto *handler : handlers) { emit handler->messageLogged(statusMessage); }
219 void CLogHandler::removePatternHandler(CLogPatternHandler *handler)
221 auto it = std::find_if(m_patternHandlers.begin(), m_patternHandlers.end(),
222 [handler](
const PatternPair &pair) { return pair.second == handler; });
223 if (it != m_patternHandlers.end())
225 it->second->deleteLater();
226 m_patternHandlers.erase(it);
232 QList<CLogPattern> result;
233 for (
const auto &pair : m_patternHandlers)
237 result.push_back(pair.first);
243 CLogPatternHandler::CLogPatternHandler(CLogHandler *parent,
const CLogPattern &pattern)
244 : QObject(parent), m_parent(parent), m_pattern(pattern)
246 connect(&m_subscriptionUpdateTimer, &QTimer::timeout,
this, &CLogPatternHandler::updateSubscription);
247 m_subscriptionUpdateTimer.start(1);
250 void CLogPatternHandler::updateSubscription()
252 if (m_subscriptionNeedsUpdate)
254 m_subscriptionNeedsUpdate =
false;
255 bool isSubscribed = isSignalConnected(QMetaMethod::fromSignal(&CLogPatternHandler::messageLogged));
257 if (isSubscribed != m_isSubscribed)
259 m_isSubscribed = isSubscribed;
260 if (m_isSubscribed) { emit m_parent->subscriptionAdded(m_pattern); }
261 else { emit m_parent->subscriptionRemoved(m_pattern); }
264 if (m_inheritFallThrough && !m_isSubscribed) { m_parent->removePatternHandler(
this); }
268 void CLogSubscriber::changeSubscription(
const CLogPattern &pattern)
270 if (CLogHandler::instance()->thread() != QThread::currentThread())
272 Q_ASSERT(thread() == QThread::currentThread());
273 singleShot(0, CLogHandler::instance(), [pattern,
self = QPointer<CLogSubscriber>(
this)]() {
274 if (
self) {
self->changeSubscription(pattern); }
280 m_handler = CLogHandler::instance()->handlerForPattern(pattern);
282 if (!m_inheritFallThrough) { m_handler->enableConsoleOutput(m_enableFallThrough); }
283 connect(m_handler.data(), &CLogPatternHandler::messageLogged,
this, &CLogSubscriber::ps_logMessage,
284 Qt::DirectConnection);
287 void CLogSubscriber::unsubscribe()
289 if (CLogHandler::instance()->thread() != QThread::currentThread())
291 Q_ASSERT(thread() == QThread::currentThread());
292 singleShot(0, CLogHandler::instance(), [
self = QPointer<CLogSubscriber>(
this)]() {
293 if (
self) {
self->unsubscribe(); }
300 if (!m_inheritFallThrough) { m_handler->inheritConsoleOutput(); }
301 m_handler->disconnect(
this);
305 void CLogSubscriber::inheritConsoleOutput()
307 if (CLogHandler::instance()->thread() != QThread::currentThread())
309 Q_ASSERT(thread() == QThread::currentThread());
310 singleShot(0, CLogHandler::instance(), [
self = QPointer<CLogSubscriber>(
this)]() {
311 if (
self) {
self->inheritConsoleOutput(); }
316 m_inheritFallThrough =
true;
317 if (m_handler) { m_handler->inheritConsoleOutput(); }
320 void CLogSubscriber::enableConsoleOutput(
bool enable)
322 if (CLogHandler::instance()->thread() != QThread::currentThread())
324 Q_ASSERT(thread() == QThread::currentThread());
325 singleShot(0, CLogHandler::instance(), [enable,
self = QPointer<CLogSubscriber>(
this)]() {
326 if (
self) {
self->enableConsoleOutput(enable); }
331 m_inheritFallThrough =
false;
332 m_enableFallThrough = enable;
333 if (m_handler) { m_handler->enableConsoleOutput(enable); }
void localMessageLogged(const swift::misc::CStatusMessage &message)
Emitted when a message is logged in this process.
void enableConsoleOutput(bool enable)
Enable or disable the default Qt handler.
QList< CLogPattern > getAllSubscriptions() const
Returns all log patterns for which there are currently subscribed log pattern handlers.
static CLogHandler * instance()
Return pointer to the CLogHandler singleton.
CLogPatternHandler * handlerForCategory(const CLogCategory &category)
Return a pattern handler for subscribing to all messages which contain the given category.
CLogPatternHandler * handlerForPattern(const CLogPattern &pattern)
Return a pattern handler for subscribing to all messages which match the given pattern.
void logLocalMessage(const swift::misc::CStatusMessage &message)
Called by our QtMessageHandler to log a message.
void logRemoteMessage(const swift::misc::CStatusMessage &message)
Called by the context to relay a message.
void remoteMessageLogged(const swift::misc::CStatusMessage &message)
Emitted when a log message is relayed from a different process.
void install(bool skipIfAlreadyInstalled=false)
Tell the CLogHandler to install itself with qInstallMessageHandler.
void messageLogged(const swift::misc::CStatusMessage &message)
Emitted when a message is logged which matches the relevant pattern.
static CLogPattern exactMatch(const CLogCategory &category)
Returns a CLogPattern which will match any message with the given category.
static CLogPattern empty()
Returns a CLogPattern which will match any message without a category.
constexpr static auto SeverityDebug
Status severities.
constexpr static auto SeverityError
Status severities.
constexpr static auto SeverityWarning
Status severities.
Free functions in swift::misc.
void topologicallySortedInsert(C &container, T &&value, F comparator)
Insert an element into a sequential container while preserving the topological ordering of the contai...
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...