Akonadi

akremotelog.cpp
1 /*
2  Copyright (c) 2018 Daniel Vrátil <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "akremotelog.h"
21 
22 #include <QString>
23 #include <QCoreApplication>
24 #include <QTimer>
25 #include <QThread>
26 #include <QDateTime>
27 #include <QLoggingCategory>
28 
29 #include <QDBusConnection>
30 #include <QDBusInterface>
31 #include <QDBusPendingCallWatcher>
32 #include <QDBusPendingReply>
33 #include <QDBusServiceWatcher>
34 
35 #include <private/instance_p.h>
36 
37 #define AKONADICONSOLE_SERVICE "org.kde.akonadiconsole"
38 #define AKONADICONSOLE_LOGGER_PATH "/logger"
39 #define AKONADICONSOLE_LOGGER_INTERFACE "org.kde.akonadiconsole.logger"
40 
41 namespace {
42 
43 class RemoteLogger : public QObject
44 {
45  Q_OBJECT
46 public:
47  explicit RemoteLogger()
48  : mWatcher(akonadiConsoleServiceName(), QDBusConnection::sessionBus(),
49  QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration)
50  {
52  this, &RemoteLogger::deleteLater);
53 
54  sInstance = this;
55 
56  // Don't do remote logging for Akonadi Console because it deadlocks it
57  if (QCoreApplication::applicationName() == QLatin1String("akonadiconsole")) {
58  return;
59  }
60 
62  this, &RemoteLogger::serviceRegistered);
64  this, &RemoteLogger::serviceUnregistered);
65 
66  mOldHandler = qInstallMessageHandler(dbusLogger);
67  }
68 
69  ~RemoteLogger()
70  {
71  sInstance = nullptr;
72 
74  qInstallMessageHandler(mOldHandler);
75 
76  mEnabled = false;
77  }
78 
79  static RemoteLogger *self()
80  {
81  return sInstance;
82  }
83 
84 private Q_SLOTS:
85  void serviceRegistered(const QString &service)
86  {
87  mAkonadiConsoleInterface = std::make_unique<QDBusInterface>(service,
88  QStringLiteral(AKONADICONSOLE_LOGGER_PATH),
89  QStringLiteral(AKONADICONSOLE_LOGGER_INTERFACE),
91  if (!mAkonadiConsoleInterface->isValid()) {
92  mAkonadiConsoleInterface.reset();
93  return;
94  }
95 
96  connect(mAkonadiConsoleInterface.get(), SIGNAL(enabledChanged(bool)), // clazy:exclude=old-style-connect
97  this, SLOT(onAkonadiConsoleLoggingEnabled(bool)));
98 
99  QTimer::singleShot(0, this, [this]() {
100  auto watcher = new QDBusPendingCallWatcher(mAkonadiConsoleInterface->asyncCall(QStringLiteral("enabled")));
102  this, [this](QDBusPendingCallWatcher *watcher) {
103  watcher->deleteLater();
104  QDBusPendingReply<bool> reply = *watcher;
105  if (reply.isError()) {
106  return;
107  }
108  onAkonadiConsoleLoggingEnabled(reply.argumentAt<0>());
109  });
110  });
111  }
112 
113  void serviceUnregistered(const QString &)
114  {
115  onAkonadiConsoleLoggingEnabled(false);
116  mAkonadiConsoleInterface.reset();
117  }
118 
119  void onAkonadiConsoleLoggingEnabled(bool enabled)
120  {
121  if (mEnabled == enabled) {
122  return;
123  }
124 
125  mEnabled = enabled;
126  if (mEnabled) {
127  // FIXME: Qt calls our categoryFilter from installFilter() but at that
128  // point we cannot refer to mOldFilter yet (as we only receive it after
129  // this call returns. So we set our category filter twice: once to get
130  // the original Qt filter and second time to force our category filter
131  // to be called when we already know the old filter.
132  mOldFilter = QLoggingCategory::installFilter(categoryFilter);
133  QLoggingCategory::installFilter(categoryFilter);
134  } else {
136  mOldFilter = nullptr;
137  }
138  }
139 
140 private:
141  QString akonadiConsoleServiceName()
142  {
143  QString service = QStringLiteral(AKONADICONSOLE_SERVICE);
144  if (Akonadi::Instance::hasIdentifier()) {
145  service += QStringLiteral("-%1").arg(Akonadi::Instance::identifier());
146  }
147  return service;
148  }
149 
150  static void categoryFilter(QLoggingCategory *cat)
151  {
152  const auto that = self();
153  if (!that) {
154  return;
155  }
156 
157  if (qstrncmp(cat->categoryName(), "org.kde.pim.", 12) == 0) {
158  cat->setEnabled(QtDebugMsg, true);
159  cat->setEnabled(QtInfoMsg, true);
160  cat->setEnabled(QtWarningMsg, true);
161  cat->setEnabled(QtCriticalMsg, true);
162  } else if (that->mOldFilter) {
163  that->mOldFilter(cat);
164  }
165  }
166 
167  static void dbusLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &msg)
168  {
169  const auto that = self();
170  if (!that) {
171  return;
172  }
173 
174  // Log to previous logger
175  that->mOldHandler(type, ctx, msg);
176 
177  if (that->mEnabled) {
178  that->mAkonadiConsoleInterface->asyncCallWithArgumentList(
179  QStringLiteral("message"),
181  QDateTime::currentMSecsSinceEpoch(), qAppName(),
182  qApp->applicationPid(), static_cast<int>(type),
183  QString::fromUtf8(ctx.category), QString::fromUtf8(ctx.file),
184  QString::fromUtf8(ctx.function), ctx.line, ctx.version, msg });
185  }
186  }
187 
188 private:
189  QDBusServiceWatcher mWatcher;
190  QLoggingCategory::CategoryFilter mOldFilter = nullptr;
191  QtMessageHandler mOldHandler = nullptr;
192  std::unique_ptr<QDBusInterface> mAkonadiConsoleInterface;
193  bool mEnabled = false;
194 
195  static RemoteLogger *sInstance;
196 };
197 
198 RemoteLogger *RemoteLogger::sInstance = nullptr;
199 
200 }
201 
202 void akInitRemoteLog()
203 {
204  Q_ASSERT(qApp->thread() == QThread::currentThread());
205 
206  if (!RemoteLogger::self()) {
207  new RemoteLogger();
208  }
209 }
210 
211 
212 #include "akremotelog.moc"
213 
void setEnabled(QtMsgType type, bool enable)
void serviceRegistered(const QString &serviceName)
void finished(QDBusPendingCallWatcher *self)
QLoggingCategory::CategoryFilter installFilter(QLoggingCategory::CategoryFilter filter)
QDBusConnection sessionBus()
qint64 currentMSecsSinceEpoch()
QString fromUtf8(const char *str, int size)
typedef CategoryFilter
void serviceUnregistered(const QString &serviceName)
void deleteLater()
const char * categoryName() const const
QVariant argumentAt(int index) const const
QThread * currentThread()
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString applicationName()
bool isError() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Jun 2 2020 23:09:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.