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:
Tobias Hunger
2015-01-23 18:23:24 +01:00
parent 4b51064b45
commit d8d93b7282
6 changed files with 325 additions and 2 deletions

View File

@@ -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)
{

View File

@@ -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();

View 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

View 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

View File

@@ -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);

View File

@@ -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