Akonadi

akremotelog.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "akremotelog.h"
8
9#include <QCoreApplication>
10#include <QDateTime>
11#include <QLoggingCategory>
12#include <QString>
13#include <QThread>
14#include <QTimer>
15
16#include <QDBusConnection>
17#include <QDBusInterface>
18#include <QDBusPendingCallWatcher>
19#include <QDBusPendingReply>
20#include <QDBusServiceWatcher>
21
22#include "private/instance_p.h"
23
24#define AKONADICONSOLE_SERVICE "org.kde.akonadiconsole"
25#define AKONADICONSOLE_LOGGER_PATH "/logger"
26#define AKONADICONSOLE_LOGGER_INTERFACE "org.kde.akonadiconsole.logger"
27
28namespace
29{
30class RemoteLogger : public QObject
31{
32 Q_OBJECT
33public:
34 explicit RemoteLogger()
35 : mWatcher(akonadiConsoleServiceName(),
36 QDBusConnection::sessionBus(),
37 QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration)
38 {
39 connect(qApp, &QCoreApplication::aboutToQuit, this, &RemoteLogger::deleteLater);
40
41 sInstance = this;
42
43 // Don't do remote logging for Akonadi Console because it deadlocks it
44 if (QCoreApplication::applicationName() == QLatin1StringView("akonadiconsole")) {
45 return;
46 }
47
48 connect(&mWatcher, &QDBusServiceWatcher::serviceRegistered, this, &RemoteLogger::serviceRegistered);
49 connect(&mWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &RemoteLogger::serviceUnregistered);
50
51 mOldHandler = qInstallMessageHandler(dbusLogger);
52 }
53
54 ~RemoteLogger() override
55 {
56 sInstance = nullptr;
57
59 qInstallMessageHandler(mOldHandler);
60
61 mEnabled = false;
62 }
63
64 static RemoteLogger *self()
65 {
66 return sInstance;
67 }
68
69private Q_SLOTS:
70 void serviceRegistered(const QString &service)
71 {
72 mAkonadiConsoleInterface = std::make_unique<QDBusInterface>(service,
73 QStringLiteral(AKONADICONSOLE_LOGGER_PATH),
74 QStringLiteral(AKONADICONSOLE_LOGGER_INTERFACE),
76 this);
77 if (!mAkonadiConsoleInterface->isValid()) {
78 mAkonadiConsoleInterface.reset();
79 return;
80 }
81
82 connect(mAkonadiConsoleInterface.get(), // clazy:exclude=old-style-connect
83 SIGNAL(enabledChanged(bool)),
84 this,
85 SLOT(onAkonadiConsoleLoggingEnabled(bool)));
86
87 QTimer::singleShot(0, this, [this]() {
88 auto watcher = new QDBusPendingCallWatcher(mAkonadiConsoleInterface->asyncCall(QStringLiteral("enabled")));
89 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
90 watcher->deleteLater();
91 QDBusPendingReply<bool> reply = *watcher;
92 if (reply.isError()) {
93 return;
94 }
95 onAkonadiConsoleLoggingEnabled(reply.argumentAt<0>());
96 });
97 });
98 }
99
100 void serviceUnregistered(const QString & /*unused*/)
101 {
102 onAkonadiConsoleLoggingEnabled(false);
103 mAkonadiConsoleInterface.reset();
104 }
105
106 void onAkonadiConsoleLoggingEnabled(bool enabled)
107 {
108 if (mEnabled == enabled) {
109 return;
110 }
111
112 mEnabled = enabled;
113 if (mEnabled) {
114 // FIXME: Qt calls our categoryFilter from installFilter() but at that
115 // point we cannot refer to mOldFilter yet (as we only receive it after
116 // this call returns. So we set our category filter twice: once to get
117 // the original Qt filter and second time to force our category filter
118 // to be called when we already know the old filter.
119 mOldFilter = QLoggingCategory::installFilter(categoryFilter);
120 QLoggingCategory::installFilter(categoryFilter);
121 } else {
123 mOldFilter = nullptr;
124 }
125 }
126
127private:
128 QString akonadiConsoleServiceName()
129 {
130 QString service = QStringLiteral(AKONADICONSOLE_SERVICE);
131 if (Akonadi::Instance::hasIdentifier()) {
132 service += QStringLiteral("-%1").arg(Akonadi::Instance::identifier());
133 }
134 return service;
135 }
136
137 static void categoryFilter(QLoggingCategory *cat)
138 {
139 auto const that = self();
140 if (!that) {
141 return;
142 }
143
144 if (qstrncmp(cat->categoryName(), "org.kde.pim.", 12) == 0) {
145 cat->setEnabled(QtDebugMsg, true);
146 cat->setEnabled(QtInfoMsg, true);
147 cat->setEnabled(QtWarningMsg, true);
148 cat->setEnabled(QtCriticalMsg, true);
149 } else if (that->mOldFilter) {
150 that->mOldFilter(cat);
151 }
152 }
153
154 static void dbusLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &msg)
155 {
156 auto const that = self();
157 if (!that) {
158 return;
159 }
160
161 // Log to previous logger
162 that->mOldHandler(type, ctx, msg);
163
164 if (that->mEnabled) {
165 that->mAkonadiConsoleInterface->asyncCallWithArgumentList(QStringLiteral("message"),
167 qAppName(),
168 qApp->applicationPid(),
169 static_cast<int>(type),
170 QString::fromUtf8(ctx.category),
171 QString::fromUtf8(ctx.file),
172 QString::fromUtf8(ctx.function),
173 ctx.line,
174 ctx.version,
175 msg});
176 }
177 }
178
179private:
180 QDBusServiceWatcher mWatcher;
181 QLoggingCategory::CategoryFilter mOldFilter = nullptr;
182 QtMessageHandler mOldHandler = nullptr;
183 std::unique_ptr<QDBusInterface> mAkonadiConsoleInterface;
184 bool mEnabled = false;
185
186 static RemoteLogger *sInstance;
187};
188
189RemoteLogger *RemoteLogger::sInstance = nullptr;
190
191} // namespace
192
193void akInitRemoteLog()
194{
195 Q_ASSERT(qApp->thread() == QThread::currentThread());
196
197 if (!RemoteLogger::self()) {
198 new RemoteLogger();
199 }
200}
201
202#include "akremotelog.moc"
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
qint64 currentMSecsSinceEpoch()
QDBusConnection sessionBus()
void finished(QDBusPendingCallWatcher *self)
QVariant argumentAt(int index) const const
bool isError() const const
void serviceRegistered(const QString &serviceName)
void serviceUnregistered(const QString &serviceName)
const char * categoryName() const const
CategoryFilter installFilter(CategoryFilter filter)
void setEnabled(QtMsgType type, bool enable)
void deleteLater()
QString arg(Args &&... args) const const
QString fromUtf8(QByteArrayView str)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QThread * currentThread()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:31:59 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.