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
|
||||
#include "windebuginterface.h"
|
||||
#endif
|
||||
#ifdef WITH_JOURNALD
|
||||
#include "journaldwatcher.h"
|
||||
#endif
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -125,6 +128,10 @@ ApplicationLauncher::ApplicationLauncher(QObject *parent)
|
||||
connect(WinDebugInterface::instance(), SIGNAL(debugOutput(qint64,QString)),
|
||||
this, SLOT(checkDebugOutput(qint64,QString)));
|
||||
#endif
|
||||
#ifdef WITH_JOURNALD
|
||||
connect(JournaldWatcher::instance(), &JournaldWatcher::journaldOutput,
|
||||
this, &ApplicationLauncher::checkDebugOutput);
|
||||
#endif
|
||||
}
|
||||
|
||||
ApplicationLauncher::~ApplicationLauncher()
|
||||
@@ -277,13 +284,13 @@ void ApplicationLauncher::cannotRetrieveDebugOutput()
|
||||
disconnect(WinDebugInterface::instance(), 0, this, 0);
|
||||
emit appendMessage(msgWinCannotRetrieveDebuggingOutput(), Utils::ErrorMessageFormat);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ApplicationLauncher::checkDebugOutput(qint64 pid, const QString &message)
|
||||
{
|
||||
if (applicationPID() == pid)
|
||||
emit appendMessage(message, Utils::DebugFormat);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ApplicationLauncher::processDone(int exitCode, QProcess::ExitStatus status)
|
||||
{
|
||||
|
@@ -87,8 +87,8 @@ private slots:
|
||||
void readStandardError();
|
||||
#ifdef Q_OS_WIN
|
||||
void cannotRetrieveDebugOutput();
|
||||
void checkDebugOutput(qint64 pid, const QString &message);
|
||||
#endif
|
||||
void checkDebugOutput(qint64 pid, const QString &message);
|
||||
void processDone(int, QProcess::ExitStatus);
|
||||
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 "deployconfiguration.h"
|
||||
#include "gcctoolchainfactories.h"
|
||||
#ifdef WITH_JOURNALD
|
||||
#include "journaldwatcher.h"
|
||||
#endif
|
||||
#include "jsonwizard/jsonwizardfactory.h"
|
||||
#include "jsonwizard/jsonwizardgeneratorfactory.h"
|
||||
#include "jsonwizard/jsonwizardpagefactory_p.h"
|
||||
@@ -284,6 +287,9 @@ public:
|
||||
QStringList m_arguments;
|
||||
QList<ProjectPanelFactory *> m_panelFactories;
|
||||
QString m_renameFileError;
|
||||
#ifdef WITH_JOURNALD
|
||||
JournaldWatcher *m_journalWatcher;
|
||||
#endif
|
||||
};
|
||||
|
||||
ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() :
|
||||
@@ -293,6 +299,9 @@ ProjectExplorerPluginPrivate::ProjectExplorerPluginPrivate() :
|
||||
m_kitManager(0),
|
||||
m_toolChainManager(0),
|
||||
m_shuttingDown(false)
|
||||
#ifdef WITH_JOURNALD
|
||||
, m_journalWatcher(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -331,6 +340,9 @@ ProjectExplorerPlugin::~ProjectExplorerPlugin()
|
||||
// Force sequence of deletion:
|
||||
delete dd->m_kitManager; // remove all the profile information
|
||||
delete dd->m_toolChainManager;
|
||||
#ifdef WITH_JOURNALD
|
||||
delete dd->m_journalWatcher;
|
||||
#endif
|
||||
ProjectPanelFactory::destroyFactories();
|
||||
delete dd;
|
||||
}
|
||||
@@ -354,6 +366,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
|
||||
|
||||
addAutoReleasedObject(new DeviceManager);
|
||||
|
||||
#ifdef WITH_JOURNALD
|
||||
dd->m_journalWatcher = new JournaldWatcher;
|
||||
#endif
|
||||
|
||||
// Add ToolChainFactories:
|
||||
#ifdef Q_OS_WIN
|
||||
addAutoReleasedObject(new WinDebugInterface);
|
||||
|
@@ -333,6 +333,13 @@ equals(TEST, 1) {
|
||||
outputparser_test.h
|
||||
}
|
||||
|
||||
journald {
|
||||
SOURCES += journaldwatcher.cpp
|
||||
HEADERS += journaldwatcher.h
|
||||
DEFINES += WITH_JOURNALD
|
||||
LIBS += -lsystemd
|
||||
}
|
||||
|
||||
macx:LIBS += -framework Carbon
|
||||
|
||||
RESOURCES += projectexplorer.qrc
|
||||
|
Reference in New Issue
Block a user