diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 1bce2e9ef..2abc1e8ed 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -1,538 +1,538 @@ /*************************************************************************** * Copyright (C) 2006 by Till Adam * * Copyright (C) 2013 by Volker Krause * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "connection.h" #include "akonadiserver_debug.h" #include #include #include #include #include #include "storage/datastore.h" #include "handler.h" #include "notificationmanager.h" #include "tracer.h" #include "collectionreferencemanager.h" #include #ifndef Q_OS_WIN #include #endif #include #include #include #include using namespace Akonadi; using namespace Akonadi::Server; #define IDLE_TIMER_TIMEOUT 180000 // 3 min static QString connectionIdentifier(Connection *c) { QString id; id.sprintf("%p", static_cast(c)); return id; } namespace { Q_GLOBAL_STATIC(QThreadStorage>, sConnectionStore) } Connection::Connection(QObject *parent) : AkThread(connectionIdentifier(this), QThread::InheritPriority, parent) , m_socketDescriptor(0) , m_socket(nullptr) , m_currentHandler(nullptr) , m_connectionState(NonAuthenticated) , m_backend(nullptr) , m_verifyCacheOnRetrieval(false) , m_idleTimer(nullptr) , m_totalTime(0) , m_connectionClosing(false) , m_reportTime(false) { } Connection::Connection(quintptr socketDescriptor, QObject *parent) : Connection(parent) { m_socketDescriptor = socketDescriptor; m_identifier = connectionIdentifier(this); // same as objectName() const QSettings settings(Akonadi::StandardDirs::serverConfigFile(), QSettings::IniFormat); m_verifyCacheOnRetrieval = settings.value(QStringLiteral("Cache/VerifyOnRetrieval"), m_verifyCacheOnRetrieval).toBool(); } Connection *Connection::self() { Q_ASSERT(sConnectionStore->hasLocalData()); return sConnectionStore->localData(); } void Connection::init() { AkThread::init(); sConnectionStore->setLocalData(this); QLocalSocket *socket = new QLocalSocket(); if (!socket->setSocketDescriptor(m_socketDescriptor)) { qCWarning(AKONADISERVER_LOG) << "Connection(" << m_identifier << ")::run: failed to set socket descriptor: " << socket->error() << "(" << socket->errorString() << ")"; delete socket; return; } m_socket = socket; connect(socket, &QLocalSocket::disconnected, this, &Connection::slotSocketDisconnected); m_idleTimer = new QTimer(this); connect(m_idleTimer, &QTimer::timeout, this, &Connection::slotConnectionIdle); if (socket->state() == QLocalSocket::ConnectedState) { QTimer::singleShot(0, this, &Connection::handleIncomingData); } else { connect(socket, &QLocalSocket::connected, this, &Connection::handleIncomingData, Qt::QueuedConnection); } try { slotSendHello(); } catch (const ProtocolException &e) { qCWarning(AKONADISERVER_LOG) << "Protocol Exception sending \"hello\":" << e.what(); m_socket->disconnectFromServer(); } } void Connection::quit() { if (QThread::currentThread()->loopLevel() > 1) { m_connectionClosing = true; Q_EMIT connectionClosing(); return; } Tracer::self()->endConnection(m_identifier, QString()); collectionReferenceManager()->removeSession(m_sessionId); delete m_socket; m_socket = nullptr; if (m_idleTimer) { m_idleTimer->stop(); } delete m_idleTimer; AkThread::quit(); } void Connection::slotSendHello() { SchemaVersion version = SchemaVersion::retrieveAll().first(); auto hello = Protocol::HelloResponsePtr::create(); hello->setServerName(QStringLiteral("Akonadi")); hello->setMessage(QStringLiteral("Not Really IMAP server")); hello->setProtocolVersion(Protocol::version()); hello->setGeneration(version.generation()); sendResponse(0, hello); } DataStore *Connection::storageBackend() { if (!m_backend) { m_backend = DataStore::self(); } return m_backend; } CollectionReferenceManager *Connection::collectionReferenceManager() { return CollectionReferenceManager::instance(); } Connection::~Connection() { quitThread(); if (m_reportTime) { reportTime(); } } void Connection::slotConnectionIdle() { Q_ASSERT(m_currentHandler == nullptr); if (m_backend && m_backend->isOpened()) { if (m_backend->inTransaction()) { // This is a programming error, the timer should not have fired. // But it is safer to abort and leave the connection open, until // a later operation causes the idle timer to fire (than crash // the akonadi server). qCDebug(AKONADISERVER_LOG) << m_sessionId << "NOT Closing idle db connection; we are in transaction"; return; } m_backend->close(); } } void Connection::slotSocketDisconnected() { // If we have active handler, wait for it to finish, then we emit the signal // from slotNewDate() if (m_currentHandler) { return; } Q_EMIT disconnected(); } void Connection::handleIncomingData() { Q_FOREVER { if (m_connectionClosing || m_socket->state() != QLocalSocket::ConnectedState) { break; } // Blocks with event loop until some data arrive, allows us to still use QTimers // and similar while waiting for some data to arrive if (m_socket->bytesAvailable() < int(sizeof(qint64))) { QEventLoop loop; connect(m_socket, &QLocalSocket::readyRead, &loop, &QEventLoop::quit); connect(m_socket, &QLocalSocket::stateChanged, &loop, &QEventLoop::quit); connect(this, &Connection::connectionClosing, &loop, &QEventLoop::quit); loop.exec(); } if (m_connectionClosing || m_socket->state() != QLocalSocket::ConnectedState) { break; } m_idleTimer->stop(); // will only open() a previously idle backend. // Otherwise, a new backend could lazily be constructed by later calls. if (!storageBackend()->isOpened()) { m_backend->open(); } QString currentCommand; while (m_socket->bytesAvailable() >= int(sizeof(qint64))) { QDataStream stream(m_socket); qint64 tag = -1; stream >> tag; // TODO: Check tag is incremental sequence Protocol::CommandPtr cmd; try { cmd = Protocol::deserialize(m_socket); } catch (const Akonadi::ProtocolException &e) { qCWarning(AKONADISERVER_LOG) << "ProtocolException:" << e.what(); slotConnectionStateChange(Server::LoggingOut); return; } catch (const std::exception &e) { qCWarning(AKONADISERVER_LOG) << "Unknown exception:" << e.what(); slotConnectionStateChange(Server::LoggingOut); return; } if (cmd->type() == Protocol::Command::Invalid) { qCWarning(AKONADISERVER_LOG) << "Received an invalid command: resetting connection"; slotConnectionStateChange(Server::LoggingOut); return; } // Tag context and collection context is not persistent. context()->setTag(-1); context()->setCollection(Collection()); if (Tracer::self()->currentTracer() != QLatin1String("null")) { - Tracer::self()->connectionInput(m_identifier, QByteArray::number(tag) + ' ' + Protocol::debugString(cmd).toUtf8()); + Tracer::self()->connectionInput(m_identifier, tag, cmd); } m_currentHandler = findHandlerForCommand(cmd->type()); if (!m_currentHandler) { qCWarning(AKONADISERVER_LOG) << "Invalid command: no such handler for" << cmd->type(); slotConnectionStateChange(Server::LoggingOut); return; } if (m_reportTime) { startTime(); } connect(m_currentHandler.data(), &Handler::connectionStateChange, this, &Connection::slotConnectionStateChange, Qt::DirectConnection); m_currentHandler->setConnection(this); m_currentHandler->setTag(tag); m_currentHandler->setCommand(cmd); try { if (!m_currentHandler->parseStream()) { try { m_currentHandler->failureResponse("Unknown error while handling a command"); } catch (...) { qCWarning(AKONADISERVER_LOG) << "Unknown error while handling a command"; m_connectionClosing = true; } } } catch (const Akonadi::Server::HandlerException &e) { if (m_currentHandler) { try { m_currentHandler->failureResponse(e.what()); } catch (...) { qCWarning(AKONADISERVER_LOG) << "Handler exception:" << e.what(); m_connectionClosing = true; } } } catch (const Akonadi::Server::Exception &e) { if (m_currentHandler) { try { m_currentHandler->failureResponse(QString::fromUtf8(e.type()) + QLatin1String(": ") + QString::fromUtf8(e.what())); } catch (...) { qCWarning(AKONADISERVER_LOG) << e.type() << "exception:" << e.what(); m_connectionClosing = true; } } } catch (const Akonadi::ProtocolException &e) { // No point trying to send anything back to client, the connection is // already messed up qCWarning(AKONADISERVER_LOG) << "Protocol exception:" << e.what(); m_connectionClosing = true; #if defined(Q_OS_LINUX) } catch (abi::__forced_unwind&) { // HACK: NPTL throws __forced_unwind during thread cancellation and // we *must* rethrow it otherwise the program aborts. Due to the issue // described in #376385 we might end up destroying (cancelling) the // thread from a nested loop executed inside parseStream() above, // so the exception raised in there gets caught by this try..catch // statement and it must be rethrown at all cost. Remove this hack // once the root problem is fixed. throw; #endif } catch (...) { qCCritical(AKONADISERVER_LOG) << "Unknown exception caught in Connection for session" << m_sessionId; if (m_currentHandler) { try { m_currentHandler->failureResponse("Unknown exception caught"); } catch (...) { qCWarning(AKONADISERVER_LOG) << "Unknown exception caught"; m_connectionClosing = true; } } } if (m_reportTime) { stopTime(currentCommand); } delete m_currentHandler; m_currentHandler = nullptr; if (m_socket->state() != QLocalSocket::ConnectedState) { Q_EMIT disconnected(); return; } if (m_connectionClosing) { break; } } // reset, arm the timer m_idleTimer->start(IDLE_TIMER_TIMEOUT); if (m_connectionClosing) { break; } } if (m_connectionClosing) { m_socket->disconnect(this); m_socket->close(); QTimer::singleShot(0, this, &Connection::quit); } } CommandContext *Connection::context() const { return const_cast(&m_context); } Handler *Connection::findHandlerForCommand(Protocol::Command::Type command) { Handler *handler = Handler::findHandlerForCommandAlwaysAllowed(command); if (handler) { return handler; } switch (m_connectionState) { case NonAuthenticated: handler = Handler::findHandlerForCommandNonAuthenticated(command); break; case Authenticated: handler = Handler::findHandlerForCommandAuthenticated(command); break; case Selected: break; case LoggingOut: break; } return handler; } void Connection::slotConnectionStateChange(ConnectionState state) { if (state == m_connectionState) { return; } m_connectionState = state; switch (m_connectionState) { case NonAuthenticated: assert(0); // can't happen, it's only the initial state, we can't go back to it break; case Authenticated: break; case Selected: break; case LoggingOut: m_socket->disconnectFromServer(); break; } } void Connection::setSessionId(const QByteArray &id) { m_identifier.sprintf("%s (%p)", id.data(), static_cast(this)); Tracer::self()->beginConnection(m_identifier, QString()); //m_streamParser->setTracerIdentifier(m_identifier); m_sessionId = id; setObjectName(QString::fromLatin1(id)); // this races with the use of objectName() in QThreadPrivate::start //thread()->setObjectName(objectName() + QStringLiteral("-Thread")); storageBackend()->setSessionId(id); storageBackend()->notificationCollector()->setSessionId(id); } QByteArray Connection::sessionId() const { return m_sessionId; } bool Connection::isOwnerResource(const PimItem &item) const { if (context()->resource().isValid() && item.collection().resourceId() == context()->resource().id()) { return true; } // fallback for older resources if (sessionId() == item.collection().resource().name().toUtf8()) { return true; } return false; } bool Connection::isOwnerResource(const Collection &collection) const { if (context()->resource().isValid() && collection.resourceId() == context()->resource().id()) { return true; } if (sessionId() == collection.resource().name().toUtf8()) { return true; } return false; } bool Connection::verifyCacheOnRetrieval() const { return m_verifyCacheOnRetrieval; } void Connection::startTime() { m_time.start(); } void Connection::stopTime(const QString &identifier) { int elapsed = m_time.elapsed(); m_totalTime += elapsed; m_totalTimeByHandler[identifier] += elapsed; m_executionsByHandler[identifier]++; qCDebug(AKONADISERVER_LOG) << identifier << " time : " << elapsed << " total: " << m_totalTime; } void Connection::reportTime() const { qCDebug(AKONADISERVER_LOG) << "===== Time report for " << m_identifier << " ====="; qCDebug(AKONADISERVER_LOG) << " total: " << m_totalTime; for (auto it = m_totalTimeByHandler.cbegin(), end = m_totalTimeByHandler.cend(); it != end; ++it) { const QString &handler = it.key(); qCDebug(AKONADISERVER_LOG) << "handler : " << handler << " time: " << m_totalTimeByHandler.value(handler) << " executions " << m_executionsByHandler.value(handler) << " avg: " << m_totalTimeByHandler.value(handler) / m_executionsByHandler.value(handler); } } void Connection::sendResponse(qint64 tag, const Protocol::CommandPtr &response) { if (Tracer::self()->currentTracer() != QLatin1String("null")) { - Tracer::self()->connectionOutput(m_identifier, QByteArray::number(tag) + ' ' + Protocol::debugString(response).toUtf8()); + Tracer::self()->connectionOutput(m_identifier, tag, response); } QDataStream stream(m_socket); stream << tag; Protocol::serialize(m_socket, response); if (!m_socket->waitForBytesWritten()) { if (m_socket->state() == QLocalSocket::ConnectedState) { throw ProtocolException("Server write timeout"); } else { // The client has disconnected before we managed to send our response, // which is not an error } } } void Connection::sendResponse(const Protocol::CommandPtr &response) { Q_ASSERT(m_currentHandler); sendResponse(m_currentHandler->tag(), response); } Protocol::CommandPtr Connection::readCommand() { while (m_socket->bytesAvailable() < (int) sizeof(qint64)) { Protocol::DataStream::waitForData(m_socket, 10000); // 10 seconds, just in case client is busy } QDataStream stream(m_socket); qint64 tag; stream >> tag; // TODO: compare tag with m_currentHandler->tag() ? return Protocol::deserialize(m_socket); } diff --git a/src/server/dbustracer.h b/src/server/dbustracer.h index 01dac0a57..95c49f047 100644 --- a/src/server/dbustracer.h +++ b/src/server/dbustracer.h @@ -1,64 +1,66 @@ /*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef AKONADI_DBUSTRACER_H #define AKONADI_DBUSTRACER_H #include #include "tracerinterface.h" namespace Akonadi { namespace Server { /** * A tracer which forwards all tracing information as dbus signals. */ class DBusTracer : public QObject, public TracerInterface { Q_OBJECT public: DBusTracer(); ~DBusTracer() override; void beginConnection(const QString &identifier, const QString &msg) override; void endConnection(const QString &identifier, const QString &msg) override; void connectionInput(const QString &identifier, const QByteArray &msg) override; void connectionOutput(const QString &identifier, const QByteArray &msg) override; void signal(const QString &signalName, const QString &msg) override; void warning(const QString &componentName, const QString &msg) override; void error(const QString &componentName, const QString &msg) override; + TracerInterface::ConnectionFormat connectionFormat() const override {return TracerInterface::Json;} + Q_SIGNALS: void connectionStarted(const QString &identifier, const QString &msg); void connectionEnded(const QString &identifier, const QString &msg); void connectionDataInput(const QString &identifier, const QString &msg); void connectionDataOutput(const QString &identifier, const QString &msg); void signalEmitted(const QString &signalName, const QString &msg); void warningEmitted(const QString &componentName, const QString &msg); void errorEmitted(const QString &componentName, const QString &msg); }; } // namespace Server } // namespace Akonadi #endif diff --git a/src/server/tracer.cpp b/src/server/tracer.cpp index 090619531..f9b9c57a5 100644 --- a/src/server/tracer.cpp +++ b/src/server/tracer.cpp @@ -1,148 +1,183 @@ /*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "tracer.h" #include #include #include "traceradaptor.h" #include "dbustracer.h" #include "filetracer.h" #include "nulltracer.h" +#include #include // #define DEFAULT_TRACER QLatin1String( "dbus" ) #define DEFAULT_TRACER QStringLiteral( "null" ) using namespace Akonadi::Server; Tracer *Tracer::mSelf = nullptr; Tracer::Tracer() : mTracerBackend(nullptr) , mSettings(new QSettings(Akonadi::StandardDirs::serverConfigFile(), QSettings::IniFormat)) { activateTracer(currentTracer()); new TracerAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/tracing"), this, QDBusConnection::ExportAdaptors); } Tracer::~Tracer() { delete mTracerBackend; mTracerBackend = nullptr; } Tracer *Tracer::self() { if (!mSelf) { mSelf = new Tracer(); } return mSelf; } void Tracer::beginConnection(const QString &identifier, const QString &msg) { mMutex.lock(); mTracerBackend->beginConnection(identifier, msg); mMutex.unlock(); } void Tracer::endConnection(const QString &identifier, const QString &msg) { mMutex.lock(); mTracerBackend->endConnection(identifier, msg); mMutex.unlock(); } void Tracer::connectionInput(const QString &identifier, const QByteArray &msg) { mMutex.lock(); mTracerBackend->connectionInput(identifier, msg); mMutex.unlock(); } +void Akonadi::Server::Tracer::connectionInput(const QString& identifier, qint64 tag, const Protocol::CommandPtr &cmd) +{ + QByteArray msg; + if (mTracerBackend->connectionFormat() == TracerInterface::Json) { + QJsonObject json; + json[QStringLiteral("tag")] = tag; + Akonadi::Protocol::toJson(cmd.data(), json); + + QJsonDocument doc(json); + + msg = doc.toJson(QJsonDocument::Indented); + } else { + msg = QByteArray::number(tag) + ' ' + Protocol::debugString(cmd).toUtf8(); + } + connectionInput(identifier, msg); +} + void Tracer::connectionOutput(const QString &identifier, const QByteArray &msg) { mMutex.lock(); mTracerBackend->connectionOutput(identifier, msg); mMutex.unlock(); } +void Akonadi::Server::Tracer::connectionOutput(const QString& identifier, qint64 tag, const Protocol::CommandPtr &cmd) +{ + QByteArray msg; + if (mTracerBackend->connectionFormat() == TracerInterface::Json) { + QJsonObject json; + json[QStringLiteral("tag")] = tag; + Akonadi::Protocol::toJson(cmd.data(), json); + + QJsonDocument doc(json); + + msg = doc.toJson(QJsonDocument::Indented); + } else { + msg = QByteArray::number(tag) + ' ' + Protocol::debugString(cmd).toUtf8(); + } + connectionOutput(identifier, msg); +} + void Tracer::signal(const QString &signalName, const QString &msg) { mMutex.lock(); mTracerBackend->signal(signalName, msg); mMutex.unlock(); } void Tracer::signal(const char *signalName, const QString &msg) { signal(QLatin1String(signalName), msg); } void Tracer::warning(const QString &componentName, const QString &msg) { mMutex.lock(); mTracerBackend->warning(componentName, msg); mMutex.unlock(); } void Tracer::error(const QString &componentName, const QString &msg) { mMutex.lock(); mTracerBackend->error(componentName, msg); mMutex.unlock(); } void Tracer::error(const char *componentName, const QString &msg) { error(QLatin1String(componentName), msg); } QString Tracer::currentTracer() const { QMutexLocker locker(&mMutex); return mSettings->value(QStringLiteral("Debug/Tracer"), DEFAULT_TRACER).toString(); } void Tracer::activateTracer(const QString &type) { QMutexLocker locker(&mMutex); delete mTracerBackend; mTracerBackend = nullptr; mSettings->setValue(QStringLiteral("Debug/Tracer"), type); mSettings->sync(); if (type == QLatin1String("file")) { const QString file = mSettings->value(QStringLiteral("Debug/File"), QStringLiteral("/dev/null")).toString(); mTracerBackend = new FileTracer(file); } else if (type == QLatin1String("null")) { mTracerBackend = new NullTracer(); } else { mTracerBackend = new DBusTracer(); } Q_ASSERT(mTracerBackend); } diff --git a/src/server/tracer.h b/src/server/tracer.h index 340473672..c86ea81c1 100644 --- a/src/server/tracer.h +++ b/src/server/tracer.h @@ -1,147 +1,158 @@ /*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef AKONADI_TRACER_H #define AKONADI_TRACER_H #include #include #include "tracerinterface.h" class QSettings; namespace Akonadi { + +namespace Protocol +{ +class Command; +using CommandPtr = QSharedPointer; +} + namespace Server { /** * The global tracer instance where all akonadi components can * send their tracing information to. * * The tracer will forward these information to the configured backends. */ class Tracer : public QObject, public TracerInterface { Q_OBJECT public: /** * Returns the global tracer instance. */ static Tracer *self(); /** * Destroys the global tracer instance. */ ~Tracer() override; public Q_SLOTS: /** * This method is called whenever a new data (imap) connection to the akonadi server * is established. * * @param identifier The unique identifier for this connection. All input and output * messages for this connection will have the same identifier. * * @param msg A message specific string. */ void beginConnection(const QString &identifier, const QString &msg) override; /** * This method is called whenever a data (imap) connection to akonadi server is * closed. * * @param identifier The unique identifier of this connection. * @param msg A message specific string. */ void endConnection(const QString &identifier, const QString &msg) override; /** * This method is called whenever the akonadi server retrieves some data from the * outside. * * @param identifier The unique identifier of the connection on which the data * is retrieved. * @param msg A message specific string. */ void connectionInput(const QString &identifier, const QByteArray &msg) override; + void connectionInput(const QString &identifier, qint64 tag, const Protocol::CommandPtr &cmd); + /** * This method is called whenever the akonadi server sends some data out to a client. * * @param identifier The unique identifier of the connection on which the * data is send. * @param msg A message specific string. */ void connectionOutput(const QString &identifier, const QByteArray &msg) override; + void connectionOutput(const QString &identifier, qint64 tag, const Protocol::CommandPtr &cmd); + /** * This method is called whenever a dbus signal is emitted on the bus. * * @param signalName The name of the signal being sent. * @param msg A message specific string. */ void signal(const QString &signalName, const QString &msg) override; /** Convenience method with internal toLatin1 cast to compile with QT_NO_CAST_FROM_ASCII. */ void signal(const char *signalName, const QString &msg); /** * This method is called whenever a component wants to output a warning. */ void warning(const QString &componentName, const QString &msg) override; /** * This method is called whenever a component wants to output an error. */ void error(const QString &componentName, const QString &msg) override; /** * Convenience method for QT_NO_CAST_FROM_ASCII usage. */ void error(const char *componentName, const QString &msg); /** * Returns the currently activated tracer type. */ QString currentTracer() const; /** * Activates the given tracer type. */ void activateTracer(const QString &type); private: Tracer(); static Tracer *mSelf; TracerInterface *mTracerBackend; mutable QMutex mMutex; QScopedPointer mSettings; }; } // namespace Server } // namespace Akonadi #endif diff --git a/src/server/tracerinterface.h b/src/server/tracerinterface.h index 49c465ae1..e6547ed27 100644 --- a/src/server/tracerinterface.h +++ b/src/server/tracerinterface.h @@ -1,108 +1,115 @@ /*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef AKONADI_TRACERINTERFACE_H #define AKONADI_TRACERINTERFACE_H class QByteArray; class QString; namespace Akonadi { namespace Server { /** * This interface can be reimplemented to deliver tracing information * of the akonadi server to the outside. * * Possible implementations: * - log file * - dbus signals * - live gui */ class TracerInterface { public: + enum ConnectionFormat { + DebugString, + Json + }; + virtual ~TracerInterface() { } /** * This method is called whenever a new data (imap) connection to the akonadi server * is established. * * @param identifier The unique identifier for this connection. All input and output * messages for this connection will have the same identifier. * * @param msg A message specific string. */ virtual void beginConnection(const QString &identifier, const QString &msg) = 0; /** * This method is called whenever a data (imap) connection to akonadi server is * closed. * * @param identifier The unique identifier of this connection. * @param msg A message specific string. */ virtual void endConnection(const QString &identifier, const QString &msg) = 0; /** * This method is called whenever the akonadi server retrieves some data from the * outside. * * @param identifier The unique identifier of the connection on which the data * is retrieved. * @param msg A message specific string. */ virtual void connectionInput(const QString &identifier, const QByteArray &msg) = 0; /** * This method is called whenever the akonadi server sends some data out to a client. * * @param identifier The unique identifier of the connection on which the * data is send. * @param msg A message specific string. */ virtual void connectionOutput(const QString &identifier, const QByteArray &msg) = 0; /** * This method is called whenever a dbus signal is emitted on the bus. * * @param signalName The name of the signal being sent. * @param msg A message specific string. */ virtual void signal(const QString &signalName, const QString &msg) = 0; /** * This method is called whenever a component wants to output a warning. */ virtual void warning(const QString &componentName, const QString &msg) = 0; /** * This method is called whenever a component wants to output an error. */ virtual void error(const QString &componentName, const QString &msg) = 0; + + virtual ConnectionFormat connectionFormat() const {return DebugString;} }; } // namespace Server } // namespace Akonadi #endif