Commit d8d93b72 authored by Tobias Hunger's avatar Tobias Hunger
Browse files

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: default avatarOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Reviewed-by: default avatarTobias Hunger <tobias.hunger@theqtcompany.com>
parent 4b51064b
......@@ -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();
......
/****************************************************************************
**
** 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
/****************************************************************************
**
** 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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment