Akonadi

akremotelog.cpp
1 /*
2  SPDX-FileCopyrightText: 2018 Daniel Vr├ítil <[email protected]>
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 
28 namespace
29 {
30 class RemoteLogger : public QObject
31 {
32  Q_OBJECT
33 public:
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() == QLatin1String("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 
69 private 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 
127 private:
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 
179 private:
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 
189 RemoteLogger *RemoteLogger::sInstance = nullptr;
190 
191 } // namespace
192 
193 void akInitRemoteLog()
194 {
195  Q_ASSERT(qApp->thread() == QThread::currentThread());
196 
197  if (!RemoteLogger::self()) {
198  new RemoteLogger();
199  }
200 }
201 
202 #include "akremotelog.moc"
void finished(QDBusPendingCallWatcher *self)
QString fromUtf8(const char *str, int size)
Q_SLOTSQ_SLOTS
qint64 currentMSecsSinceEpoch()
bool isError() const const
Definition: item.h:32
void serviceUnregistered(const QString &serviceName)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
QDBusConnection sessionBus()
QThread * currentThread()
typedef CategoryFilter
void serviceRegistered(const QString &serviceName)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const char * categoryName() const const
void setEnabled(QtMsgType type, bool enable)
QVariant argumentAt(int index) const const
QLoggingCategory::CategoryFilter installFilter(QLoggingCategory::CategoryFilter filter)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jul 2 2022 06:41:47 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.