forked from qt-creator/qt-creator
Retrieve output from journald
Qt nowadays logs into journald in some setups, so retrieve the output from there and put it into the application output pane. Change-Id: Ia2199a1420e1d80541f2f455e242d0b473922125 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
This commit is contained in:
@@ -32,6 +32,9 @@
|
|||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include "windebuginterface.h"
|
#include "windebuginterface.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
#include "journaldwatcher.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -125,6 +128,10 @@ ApplicationLauncher::ApplicationLauncher(QObject *parent)
|
|||||||
connect(WinDebugInterface::instance(), SIGNAL(debugOutput(qint64,QString)),
|
connect(WinDebugInterface::instance(), SIGNAL(debugOutput(qint64,QString)),
|
||||||
this, SLOT(checkDebugOutput(qint64,QString)));
|
this, SLOT(checkDebugOutput(qint64,QString)));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
connect(JournaldWatcher::instance(), &JournaldWatcher::journaldOutput,
|
||||||
|
this, &ApplicationLauncher::checkDebugOutput);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLauncher::~ApplicationLauncher()
|
ApplicationLauncher::~ApplicationLauncher()
|
||||||
@@ -277,13 +284,13 @@ void ApplicationLauncher::cannotRetrieveDebugOutput()
|
|||||||
disconnect(WinDebugInterface::instance(), 0, this, 0);
|
disconnect(WinDebugInterface::instance(), 0, this, 0);
|
||||||
emit appendMessage(msgWinCannotRetrieveDebuggingOutput(), Utils::ErrorMessageFormat);
|
emit appendMessage(msgWinCannotRetrieveDebuggingOutput(), Utils::ErrorMessageFormat);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ApplicationLauncher::checkDebugOutput(qint64 pid, const QString &message)
|
void ApplicationLauncher::checkDebugOutput(qint64 pid, const QString &message)
|
||||||
{
|
{
|
||||||
if (applicationPID() == pid)
|
if (applicationPID() == pid)
|
||||||
emit appendMessage(message, Utils::DebugFormat);
|
emit appendMessage(message, Utils::DebugFormat);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void ApplicationLauncher::processDone(int exitCode, QProcess::ExitStatus status)
|
void ApplicationLauncher::processDone(int exitCode, QProcess::ExitStatus status)
|
||||||
{
|
{
|
||||||
|
@@ -87,8 +87,8 @@ private slots:
|
|||||||
void readStandardError();
|
void readStandardError();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
void cannotRetrieveDebugOutput();
|
void cannotRetrieveDebugOutput();
|
||||||
void checkDebugOutput(qint64 pid, const QString &message);
|
|
||||||
#endif
|
#endif
|
||||||
|
void checkDebugOutput(qint64 pid, const QString &message);
|
||||||
void processDone(int, QProcess::ExitStatus);
|
void processDone(int, QProcess::ExitStatus);
|
||||||
void bringToForeground();
|
void bringToForeground();
|
||||||
|
|
||||||
|
221
src/plugins/projectexplorer/journaldwatcher.cpp
Normal file
221
src/plugins/projectexplorer/journaldwatcher.cpp
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://www.qt.io/licensing. For further information
|
||||||
|
** use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "journaldwatcher.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
|
||||||
|
#include <systemd/sd-journal.h>
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
JournaldWatcher *JournaldWatcher::m_instance = 0;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class JournaldWatcherPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JournaldWatcherPrivate() :
|
||||||
|
m_journalContext(0),
|
||||||
|
m_notifier(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~JournaldWatcherPrivate()
|
||||||
|
{
|
||||||
|
teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setup();
|
||||||
|
void teardown();
|
||||||
|
|
||||||
|
JournaldWatcher::LogEntry retrieveEntry();
|
||||||
|
|
||||||
|
class SubscriberInformation {
|
||||||
|
public:
|
||||||
|
SubscriberInformation(QObject *sr, const JournaldWatcher::Subscription &sn) :
|
||||||
|
subscriber(sr), subscription(sn)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QObject *subscriber;
|
||||||
|
JournaldWatcher::Subscription subscription;
|
||||||
|
};
|
||||||
|
QList<SubscriberInformation> m_subscriptions;
|
||||||
|
|
||||||
|
sd_journal *m_journalContext;
|
||||||
|
QSocketNotifier *m_notifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool JournaldWatcherPrivate::setup()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!m_journalContext, return false);
|
||||||
|
int r = sd_journal_open(&m_journalContext, 0);
|
||||||
|
if (r != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
r = sd_journal_seek_tail(m_journalContext);
|
||||||
|
if (r != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Work around https://bugs.freedesktop.org/show_bug.cgi?id=64614
|
||||||
|
sd_journal_previous(m_journalContext);
|
||||||
|
|
||||||
|
int fd = sd_journal_get_fd(m_journalContext);
|
||||||
|
if (fd < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JournaldWatcherPrivate::teardown()
|
||||||
|
{
|
||||||
|
delete m_notifier;
|
||||||
|
m_notifier = 0;
|
||||||
|
|
||||||
|
if (m_journalContext) {
|
||||||
|
sd_journal_close(m_journalContext);
|
||||||
|
m_journalContext = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JournaldWatcher::LogEntry JournaldWatcherPrivate::retrieveEntry()
|
||||||
|
{
|
||||||
|
JournaldWatcher::LogEntry result;
|
||||||
|
|
||||||
|
// Advance:
|
||||||
|
int r = sd_journal_next(m_journalContext);
|
||||||
|
if (r == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
const void *rawData;
|
||||||
|
size_t length;
|
||||||
|
SD_JOURNAL_FOREACH_DATA(m_journalContext, rawData, length) {
|
||||||
|
QByteArray tmp = QByteArray::fromRawData(static_cast<const char *>(rawData), length);
|
||||||
|
int offset = tmp.indexOf('=');
|
||||||
|
if (offset < 0)
|
||||||
|
continue;
|
||||||
|
result.insert(tmp.left(offset), tmp.mid(offset + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
using namespace Internal;
|
||||||
|
|
||||||
|
static JournaldWatcherPrivate *d = 0;
|
||||||
|
|
||||||
|
JournaldWatcher::~JournaldWatcher()
|
||||||
|
{
|
||||||
|
d->teardown();
|
||||||
|
|
||||||
|
m_instance = 0;
|
||||||
|
|
||||||
|
delete d;
|
||||||
|
d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JournaldWatcher *JournaldWatcher::instance()
|
||||||
|
{
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JournaldWatcher::subscribe(QObject *subscriber, const Subscription &subscription)
|
||||||
|
{
|
||||||
|
// Check that we do not have that subscriber yet:
|
||||||
|
int pos = Utils::indexOf(d->m_subscriptions,
|
||||||
|
[subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) {
|
||||||
|
return info.subscriber == subscriber;
|
||||||
|
});
|
||||||
|
QTC_ASSERT(pos >= 0, return false);
|
||||||
|
|
||||||
|
d->m_subscriptions.append(JournaldWatcherPrivate::SubscriberInformation(subscriber, subscription));
|
||||||
|
connect(subscriber, &QObject::destroyed, m_instance, &JournaldWatcher::unsubscribe);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JournaldWatcher::unsubscribe(QObject *subscriber)
|
||||||
|
{
|
||||||
|
int pos = Utils::indexOf(d->m_subscriptions,
|
||||||
|
[subscriber](const JournaldWatcherPrivate::SubscriberInformation &info) {
|
||||||
|
return info.subscriber == subscriber;
|
||||||
|
});
|
||||||
|
if (pos < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->m_subscriptions.removeAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
JournaldWatcher::JournaldWatcher()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!m_instance, return);
|
||||||
|
|
||||||
|
d = new JournaldWatcherPrivate;
|
||||||
|
m_instance = this;
|
||||||
|
|
||||||
|
if (!d->setup())
|
||||||
|
d->teardown();
|
||||||
|
else
|
||||||
|
connect(d->m_notifier, &QSocketNotifier::activated, m_instance, &JournaldWatcher::handleEntry);
|
||||||
|
m_instance->handleEntry(); // advance to the end of file...
|
||||||
|
}
|
||||||
|
|
||||||
|
void JournaldWatcher::handleEntry()
|
||||||
|
{
|
||||||
|
// process events:
|
||||||
|
if (!d->m_notifier || sd_journal_process(d->m_journalContext) == SD_JOURNAL_NOP)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LogEntry logEntry;
|
||||||
|
forever {
|
||||||
|
logEntry = d->retrieveEntry();
|
||||||
|
if (logEntry.isEmpty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
foreach (const JournaldWatcherPrivate::SubscriberInformation &info, d->m_subscriptions)
|
||||||
|
info.subscription(logEntry);
|
||||||
|
|
||||||
|
QByteArray tmp = logEntry.value(QByteArrayLiteral("_PID"));
|
||||||
|
quint64 pid = tmp.isEmpty() ? 0 : QString::fromLatin1(tmp).toInt();
|
||||||
|
|
||||||
|
QString message = QString::fromUtf8(logEntry.value(QByteArrayLiteral("MESSAGE")));
|
||||||
|
|
||||||
|
emit journaldOutput(pid, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
72
src/plugins/projectexplorer/journaldwatcher.h
Normal file
72
src/plugins/projectexplorer/journaldwatcher.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://www.qt.io/licensing. For further information
|
||||||
|
** use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef JOURNALDWATCHER_H
|
||||||
|
#define JOURNALDWATCHER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
class ProjectExplorerPlugin;
|
||||||
|
|
||||||
|
class JournaldWatcher : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef QMap<QByteArray, QByteArray> LogEntry;
|
||||||
|
typedef std::function<void(LogEntry)> Subscription;
|
||||||
|
|
||||||
|
~JournaldWatcher();
|
||||||
|
|
||||||
|
static JournaldWatcher *instance();
|
||||||
|
|
||||||
|
static bool subscribe(QObject *subscriber, const Subscription &subscription);
|
||||||
|
static void unsubscribe(QObject *subscriber);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void journaldOutput(quint64 pid, const QString &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
JournaldWatcher();
|
||||||
|
|
||||||
|
void handleEntry();
|
||||||
|
|
||||||
|
static JournaldWatcher *m_instance;
|
||||||
|
|
||||||
|
friend class ProjectExplorerPlugin;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
|
#endif // JOURNALDWATCHER_H
|
@@ -36,6 +36,9 @@
|
|||||||
#include "deployablefile.h"
|
#include "deployablefile.h"
|
||||||
#include "deployconfiguration.h"
|
#include "deployconfiguration.h"
|
||||||
#include "gcctoolchainfactories.h"
|
#include "gcctoolchainfactories.h"
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
#include "journaldwatcher.h"
|
||||||
|
#endif
|
||||||
#include "jsonwizard/jsonwizardfactory.h"
|
#include "jsonwizard/jsonwizardfactory.h"
|
||||||
#include "jsonwizard/jsonwizardgeneratorfactory.h"
|
#include "jsonwizard/jsonwizardgeneratorfactory.h"
|
||||||
#include "jsonwizard/jsonwizardpagefactory_p.h"
|
#include "jsonwizard/jsonwizardpagefactory_p.h"
|
||||||
@@ -284,6 +287,9 @@ public:
|
|||||||
QStringList m_arguments;
|
QStringList m_arguments;
|
||||||
QList<ProjectPanelFactory *> m_panelFactories;
|
QList<ProjectPanelFactory *> m_panelFactories;
|
||||||
QString m_renameFileError;
|
QString m_renameFileError;
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
JournaldWatcher *m_journalWatcher;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() :
|
ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() :
|
||||||
@@ -293,6 +299,9 @@ ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() :
|
|||||||
m_kitManager(0),
|
m_kitManager(0),
|
||||||
m_toolChainManager(0),
|
m_toolChainManager(0),
|
||||||
m_shuttingDown(false)
|
m_shuttingDown(false)
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
, m_journalWatcher(0)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,6 +340,9 @@ ProjectExplorerPlugin::~ProjectExplorerPlugin()
|
|||||||
// Force sequence of deletion:
|
// Force sequence of deletion:
|
||||||
delete dd->m_kitManager; // remove all the profile information
|
delete dd->m_kitManager; // remove all the profile information
|
||||||
delete dd->m_toolChainManager;
|
delete dd->m_toolChainManager;
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
delete dd->m_journalWatcher;
|
||||||
|
#endif
|
||||||
ProjectPanelFactory::destroyFactories();
|
ProjectPanelFactory::destroyFactories();
|
||||||
delete dd;
|
delete dd;
|
||||||
}
|
}
|
||||||
@@ -354,6 +366,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
|
|||||||
|
|
||||||
addAutoReleasedObject(new DeviceManager);
|
addAutoReleasedObject(new DeviceManager);
|
||||||
|
|
||||||
|
#ifdef WITH_JOURNALD
|
||||||
|
dd->m_journalWatcher = new JournaldWatcher;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add ToolChainFactories:
|
// Add ToolChainFactories:
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
addAutoReleasedObject(new WinDebugInterface);
|
addAutoReleasedObject(new WinDebugInterface);
|
||||||
|
@@ -333,6 +333,13 @@ equals(TEST, 1) {
|
|||||||
outputparser_test.h
|
outputparser_test.h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
journald {
|
||||||
|
SOURCES += journaldwatcher.cpp
|
||||||
|
HEADERS += journaldwatcher.h
|
||||||
|
DEFINES += WITH_JOURNALD
|
||||||
|
LIBS += -lsystemd
|
||||||
|
}
|
||||||
|
|
||||||
macx:LIBS += -framework Carbon
|
macx:LIBS += -framework Carbon
|
||||||
|
|
||||||
RESOURCES += projectexplorer.qrc
|
RESOURCES += projectexplorer.qrc
|
||||||
|
Reference in New Issue
Block a user