logger: Refactor the logging framework
authorManuel Nickschas <sputnick@quassel-irc.org>
Wed, 20 Jun 2018 23:36:27 +0000 (01:36 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Thu, 12 Jul 2018 20:49:25 +0000 (22:49 +0200)
* Rename the previous Logger class to LogMessage (since that is what
  it is), and consolidate logging functionality, including the
  various output methods, setup of log file etc., into a new
  Logger class.

* Hold the "debug log", i.e. the collected log messages, in the Logger
  class instead of in the Client. Adapt the DebugLogWidget accordingly.
  Messages will not be kept in quasselcore, as we wouldn't have a way
  to display them otherwise (and we don't want to accumulate lots of
  data over months of the core running).

* Support early log messages. Instantiate the Logger right at the
  start of main(), and keep all log messages until the Quassel class
  is initialized and we can rely on command-line options to configure
  backends accordingly. Accumulated messages will be output once the
  Logger is configured.

30 files changed:
src/client/client.cpp
src/client/client.h
src/client/clientauthhandler.cpp
src/common/CMakeLists.txt
src/common/basichandler.cpp
src/common/logger.cpp
src/common/logger.h
src/common/logmessage.cpp [new file with mode: 0644]
src/common/logmessage.h [new file with mode: 0644]
src/common/main.cpp
src/common/quassel.cpp
src/common/quassel.h
src/core/abstractsqlstorage.cpp
src/core/cipher.cpp
src/core/core.cpp
src/core/coreauthhandler.cpp
src/core/corebasichandler.cpp
src/core/coresession.cpp
src/core/coresessioneventprocessor.cpp
src/core/identserver.cpp
src/core/ldapauthenticator.cpp
src/core/postgresqlstorage.cpp
src/core/sqlauthenticator.cpp
src/core/sqlitestorage.cpp
src/core/sslserver.cpp
src/qtui/debuglogwidget.cpp
src/qtui/debuglogwidget.h
src/qtui/mainwin.cpp
src/qtui/qtui.h
src/qtui/qtuiapplication.cpp

index 32f599d..c3c4385 100644 (file)
@@ -114,8 +114,7 @@ Client::Client(QObject *parent)
     _messageProcessor(0),
     _coreAccountModel(new CoreAccountModel(this)),
     _coreConnection(new CoreConnection(this)),
     _messageProcessor(0),
     _coreAccountModel(new CoreAccountModel(this)),
     _coreConnection(new CoreConnection(this)),
-    _connected(false),
-    _debugLog(&_debugLogBuffer)
+    _connected(false)
 {
     _signalProxy->synchronize(_ircListHelper);
 }
 {
     _signalProxy->synchronize(_ircListHelper);
 }
@@ -753,48 +752,3 @@ void Client::corePasswordChanged(PeerPtr, bool success)
         coreAccountModel()->save();
     emit passwordChanged(success);
 }
         coreAccountModel()->save();
     emit passwordChanged(success);
 }
-
-
-#if QT_VERSION < 0x050000
-void Client::logMessage(QtMsgType type, const char *msg)
-{
-    fprintf(stderr, "%s\n", msg);
-    fflush(stderr);
-    if (type == QtFatalMsg) {
-        Quassel::logFatalMessage(msg);
-    }
-    else {
-        QString msgString = QString("%1\n").arg(msg);
-
-        //Check to see if there is an instance around, else we risk recursions
-        //when calling instance() and creating new ones.
-        if (!instanceExists())
-            return;
-
-        instance()->_debugLog << msgString;
-        emit instance()->logUpdated(msgString);
-    }
-}
-#else
-void Client::logMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
-{
-    Q_UNUSED(context);
-
-    fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
-    fflush(stderr);
-    if (type == QtFatalMsg) {
-        Quassel::logFatalMessage(msg.toLocal8Bit().constData());
-    }
-    else {
-        QString msgString = QString("%1\n").arg(msg);
-
-        //Check to see if there is an instance around, else we risk recursions
-        //when calling instance() and creating new ones.
-        if (!instanceExists())
-            return;
-
-        instance()->_debugLog << msgString;
-        emit instance()->logUpdated(msgString);
-    }
-}
-#endif
index f8baa98..7850b0f 100644 (file)
@@ -167,13 +167,6 @@ public:
         emit showIgnoreList(ignoreRule);
     }
 
         emit showIgnoreList(ignoreRule);
     }
 
-#if QT_VERSION < 0x050000
-    static void logMessage(QtMsgType type, const char *msg);
-#else
-    static void logMessage(QtMsgType, const QMessageLogContext&, const QString&);
-#endif
-    static inline const QString &debugLog() { return instance()->_debugLogBuffer; }
-
     void displayChannelList(NetworkId networkId) {
         emit showChannelList(networkId);
     }
     void displayChannelList(NetworkId networkId) {
         emit showChannelList(networkId);
     }
@@ -224,8 +217,6 @@ signals:
     void requestCreateNetwork(const NetworkInfo &info, const QStringList &persistentChannels = QStringList());
     void requestRemoveNetwork(NetworkId);
 
     void requestCreateNetwork(const NetworkInfo &info, const QStringList &persistentChannels = QStringList());
     void requestRemoveNetwork(NetworkId);
 
-    void logUpdated(const QString &msg);
-
     //! Emitted when a buffer has been marked as read
     /** This is currently triggered by setting lastSeenMsg, either local or remote,
      *  or by bringing the window to front.
     //! Emitted when a buffer has been marked as read
     /** This is currently triggered by setting lastSeenMsg, either local or remote,
      *  or by bringing the window to front.
@@ -327,9 +318,6 @@ private:
 
     bool _connected;
 
 
     bool _connected;
 
-    QString _debugLogBuffer;
-    QTextStream _debugLog;
-
     QList<QPair<BufferInfo, QString> > _userInputBuffer;
 
     friend class CoreConnection;
     QList<QPair<BufferInfo, QString> > _userInputBuffer;
 
     friend class CoreConnection;
index 34404c9..322f58a 100644 (file)
@@ -32,7 +32,7 @@
 
 #include "client.h"
 #include "clientsettings.h"
 
 #include "client.h"
 #include "clientsettings.h"
-#include "logger.h"
+#include "logmessage.h"
 #include "peerfactory.h"
 
 #if QT_VERSION < 0x050000
 #include "peerfactory.h"
 
 #if QT_VERSION < 0x050000
index d330d2c..caca158 100644 (file)
@@ -24,6 +24,7 @@ set(SOURCES
     irclisthelper.cpp
     ircuser.cpp
     logger.cpp
     irclisthelper.cpp
     ircuser.cpp
     logger.cpp
+    logmessage.cpp
     message.cpp
     messageevent.cpp
     network.cpp
     message.cpp
     messageevent.cpp
     network.cpp
index df5eb99..fe7531d 100644 (file)
 
 #include "basichandler.h"
 
 
 #include "basichandler.h"
 
+#include <QDebug>
 #include <QMetaMethod>
 
 #include <QMetaMethod>
 
-#include "logger.h"
-
 BasicHandler::BasicHandler(QObject *parent)
     : QObject(parent),
     _defaultHandler(-1),
 BasicHandler::BasicHandler(QObject *parent)
     : QObject(parent),
     _defaultHandler(-1),
index 5462355..ed8d802 100644 (file)
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
-#include <QFile>
-#include <QTextStream>
-#include <QDateTime>
+#include <iostream>
 
 #ifdef HAVE_SYSLOG
 #  include <syslog.h>
 #endif
 
 
 #ifdef HAVE_SYSLOG
 #  include <syslog.h>
 #endif
 
+#include <QByteArray>
+#include <QDateTime>
+#include <QDebug>
+#include <QFile>
+
 #include "logger.h"
 #include "quassel.h"
 
 #include "logger.h"
 #include "quassel.h"
 
-Logger::~Logger()
+namespace {
+
+QByteArray msgWithTime(const Logger::LogEntry &msg)
 {
 {
-    log();
+    return (msg.timeStamp.toString("yyyy-MM-dd hh:mm:ss ") + msg.message + "\n").toUtf8();
+};
+
 }
 
 
 }
 
 
-void Logger::log()
+Logger::Logger(QObject *parent)
+    : QObject(parent)
 {
 {
-    if (_logLevel < Quassel::logLevel())
-        return;
+    static bool registered = []() {
+        qRegisterMetaType<LogEntry>();
+        return true;
+    }();
+    Q_UNUSED(registered)
 
 
-    switch (_logLevel) {
-    case Quassel::DebugLevel:
-        _buffer.prepend("Debug: ");
-        break;
-    case Quassel::InfoLevel:
-        _buffer.prepend("Info: ");
-        break;
-    case Quassel::WarningLevel:
-        _buffer.prepend("Warning: ");
-        break;
-    case Quassel::ErrorLevel:
-        _buffer.prepend("Error: ");
-        break;
-    default:
-        break;
-    }
+    connect(this, SIGNAL(messageLogged(Logger::LogEntry)), this, SLOT(onMessageLogged(Logger::LogEntry)));
 
 
-#ifdef HAVE_SYSLOG
-    if (Quassel::logToSyslog()) {
-        int prio;
-        switch (_logLevel) {
-        case Quassel::DebugLevel:
-            prio = LOG_DEBUG;
-            break;
-        case Quassel::InfoLevel:
-            prio = LOG_INFO;
-            break;
-        case Quassel::WarningLevel:
-            prio = LOG_WARNING;
-            break;
-        case Quassel::ErrorLevel:
-            prio = LOG_ERR;
-            break;
-        default:
-            prio = LOG_INFO;
-            break;
+#if QT_VERSION < 0x050000
+    qInstallMsgHandler(Logger::messageHandler);
+#else
+    qInstallMessageHandler(Logger::messageHandler);
+#endif
+}
+
+
+Logger::~Logger()
+{
+    // If we're not initialized yet, output pending messages so they don't get lost
+    if (!_initialized) {
+        for (auto &&message : _messages) {
+            std::cerr << msgWithTime(message).constData();
         }
         }
-        syslog(prio|LOG_USER, "%s", qPrintable(_buffer));
     }
     }
-#endif
+}
+
 
 
-    // if we neither use syslog nor have a logfile we log to stdout
+std::vector<Logger::LogEntry> Logger::messages() const
+{
+    return _messages;
+}
+
+
+bool Logger::setup(bool keepMessages)
+{
+    _keepMessages = keepMessages;
 
 
-    if (Quassel::logFile() || !Quassel::logToSyslog()) {
-        _buffer.prepend(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss "));
+    // Set maximum level for output (we still store/announce all messages for client-side filtering)
+    if (Quassel::isOptionSet("loglevel")) {
+        QString level = Quassel::optionValue("loglevel").toLower();
+        if (level == "debug")
+            _outputLevel = LogLevel::Debug;
+        else if (level == "info")
+            _outputLevel = LogLevel::Info;
+        else if (level == "warning")
+            _outputLevel = LogLevel::Warning;
+        else if (level == "error")
+            _outputLevel = LogLevel::Error;
+        else {
+            qCritical() << qPrintable(tr("Invalid log level %1; supported are Debug|Info|Warning|Error").arg(level));
+            return false;
+        }
+    }
 
 
-        QTextStream out(stdout);
-        if (Quassel::logFile() && Quassel::logFile()->isOpen()) {
-            _buffer.remove(QChar('\n'));
-            out.setDevice(Quassel::logFile());
+    QString logfilename = Quassel::optionValue("logfile");
+    if (!logfilename.isEmpty()) {
+        _logFile.setFileName(logfilename);
+        if (!_logFile.open(QFile::Append|QFile::Unbuffered|QFile::Text)) {
+            qCritical() << qPrintable(tr("Could not open log file \"%1\": %2").arg(logfilename, _logFile.errorString()));
+        }
+    }
+    if (!_logFile.isOpen()) {
+        if (!_logFile.open(stderr, QFile::WriteOnly|QFile::Unbuffered|QFile::Text)) {
+            qCritical() << qPrintable(tr("Cannot write to stderr: %1").arg(_logFile.errorString()));
         }
         }
+    }
+
+#ifdef HAVE_SYSLOG
+    _syslogEnabled = Quassel::isOptionSet("syslog");
+#endif
+
+    _initialized = true;
 
 
-        out << _buffer << endl;
+    // Now that we've setup our logging backends, output pending messages
+    for (auto &&message : _messages) {
+        outputMessage(message);
     }
     }
+    if (!_keepMessages) {
+        _messages.clear();
+    }
+
+    return true;
 }
 
 
 #if QT_VERSION < 0x050000
 }
 
 
 #if QT_VERSION < 0x050000
-void Logger::logMessage(QtMsgType type, const char *msg)
+void Logger::messageHandler(QtMsgType type, const char *message)
+#else
+void Logger::messageHandler(QtMsgType type, const QMessageLogContext &, const QString &message)
+#endif
+{
+    Quassel::instance()->logger()->handleMessage(type, message);
+}
+
+
+void Logger::handleMessage(QtMsgType type, const QString &msg)
 {
     switch (type) {
     case QtDebugMsg:
 {
     switch (type) {
     case QtDebugMsg:
-        Logger(Quassel::DebugLevel) << msg;
+        handleMessage(LogLevel::Debug, msg);
         break;
         break;
+#if QT_VERSION >= 0x050500
+    case QtInfoMsg:
+        handleMessage(LogLevel::Info, msg);
+        break;
+#endif
     case QtWarningMsg:
     case QtWarningMsg:
-        Logger(Quassel::WarningLevel) << msg;
+        handleMessage(LogLevel::Warning, msg);
         break;
     case QtCriticalMsg:
         break;
     case QtCriticalMsg:
-        Logger(Quassel::ErrorLevel) << msg;
+        handleMessage(LogLevel::Error, msg);
         break;
     case QtFatalMsg:
         break;
     case QtFatalMsg:
-        Logger(Quassel::ErrorLevel) << msg;
-        Quassel::logFatalMessage(msg);
-        return;
+        handleMessage(LogLevel::Fatal, msg);
+        break;
     }
 }
     }
 }
-#else
-void Logger::logMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+
+
+void Logger::handleMessage(LogLevel level, const QString &msg)
 {
 {
-    Q_UNUSED(context)
+    QString logString;
 
 
-    switch (type) {
-    case QtDebugMsg:
-        Logger(Quassel::DebugLevel) << msg.toLocal8Bit().constData();
+    switch (level) {
+    case LogLevel::Debug:
+        logString = "[Debug] ";
         break;
         break;
-#if QT_VERSION >= 0x050500
-    case QtInfoMsg:
-        Logger(Quassel::InfoLevel) << msg.toLocal8Bit().constData();
+    case LogLevel::Info:
+        logString = "[Info ] ";
         break;
         break;
-#endif
-    case QtWarningMsg:
-        Logger(Quassel::WarningLevel) << msg.toLocal8Bit().constData();
+    case LogLevel::Warning:
+        logString = "[Warn ] ";
         break;
         break;
-    case QtCriticalMsg:
-        Logger(Quassel::ErrorLevel) << msg.toLocal8Bit().constData();
+    case LogLevel::Error:
+        logString = "[Error] ";
+        break;
+    case LogLevel::Fatal:
+        logString = "[FATAL] ";
         break;
         break;
-    case QtFatalMsg:
-        Logger(Quassel::ErrorLevel) << msg.toLocal8Bit().constData();
-        Quassel::logFatalMessage(msg.toLocal8Bit().constData());
-        return;
     }
     }
+
+    // Use signal connection to make this method thread-safe
+    emit messageLogged({QDateTime::currentDateTime(), level, logString += msg});
 }
 }
+
+
+void Logger::onMessageLogged(const LogEntry &message)
+{
+    if (_keepMessages) {
+        _messages.push_back(message);
+    }
+
+    // If setup() wasn't called yet, just store the message - will be output later
+    if (_initialized) {
+        outputMessage(message);
+    }
+}
+
+
+void Logger::outputMessage(const LogEntry &message)
+{
+    if (message.logLevel < _outputLevel) {
+        return;
+    }
+
+#ifdef HAVE_SYSLOG
+    if (_syslogEnabled) {
+        int prio{LOG_INFO};
+        switch (message.logLevel) {
+        case LogLevel::Debug:
+            prio = LOG_DEBUG;
+            break;
+        case LogLevel::Info:
+            prio = LOG_INFO;
+            break;
+        case LogLevel::Warning:
+            prio = LOG_WARNING;
+            break;
+        case LogLevel::Error:
+            prio = LOG_ERR;
+            break;
+        case LogLevel::Fatal:
+            prio = LOG_CRIT;
+        }
+        syslog(prio|LOG_USER, "%s", qPrintable(message.message));
+    }
 #endif
 #endif
+
+    if (!_logFile.fileName().isEmpty() || !_syslogEnabled) {
+        _logFile.write(msgWithTime(message));
+    }
+
+#ifndef Q_OS_MAC
+    // For fatal messages, write log to dump file
+    if (message.logLevel == LogLevel::Fatal) {
+        QFile dumpFile{Quassel::instance()->coreDumpFileName()};
+        if (dumpFile.open(QIODevice::Append)) {
+            dumpFile.write(msgWithTime(message));
+            dumpFile.close();
+        }
+    }
+#endif
+
+}
index 33375fe..8c1f2f0 100644 (file)
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
-#ifndef LOGGER_H
-#define LOGGER_H
+#pragma once
 
 
-#include <QStringList>
-#include <QTextStream>
+#include <vector>
 
 
-#include "quassel.h"
-#include "types.h"
+#include <QDateTime>
+#include <QFile>
+#include <QMetaType>
+#include <QObject>
+#include <QString>
 
 
-class Logger
+/**
+ * The Logger class encapsulates the various configured logging backends.
+ */
+class Logger : public QObject
 {
 {
+    Q_OBJECT
+
 public:
 public:
-    inline Logger(Quassel::LogLevel level) : _stream(&_buffer, QIODevice::WriteOnly), _logLevel(level) {}
-    ~Logger();
+    Logger(QObject *parent = nullptr);
+    ~Logger() override;
+
+    enum class LogLevel {
+        Debug,
+        Info,
+        Warning,
+        Error,
+        Fatal
+    };
+
+    struct LogEntry {
+        QDateTime timeStamp;
+        LogLevel logLevel;
+        QString message;
+    };
+
+    /**
+     * Initial setup, to be called ones command line options are available.
+     *
+     * Sets up the log file if appropriate. Outputs the log messages already accumulated since
+     * construction. If @c keepMessages is false, deletes the accumulated messages afterwards,
+     * and won't store further ones.
+     *
+     * @param keepMessages Whether messages should be kept
+     * @returns true, if initialization was successful
+     */
+    bool setup(bool keepMessages);
+
+    /**
+     * Accesses the stores log messages, e.g. for consumption by DebugLogWidget.
+     *
+     * @returns The accumuates log messages
+     */
+    std::vector<Logger::LogEntry> messages() const;
 
 #if QT_VERSION < 0x050000
 
 #if QT_VERSION < 0x050000
-    static void logMessage(QtMsgType type, const char *msg);
+    static void messageHandler(QtMsgType type, const char *message);
 #else
 #else
-    static void logMessage(QtMsgType, const QMessageLogContext&, const QString&);
+    static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
 #endif
 
 #endif
 
-    template<typename T>
-    inline Logger &operator<<(const T &value) { _stream << value << " "; return *this; }
-    inline Logger &operator<<(const QStringList &t) { _stream << t.join(" ") << " "; return *this; }
-    inline Logger &operator<<(bool t) { _stream << (t ? "true" : "false") << " "; return *this; }
-
-private:
-    void log();
-    QTextStream _stream;
-    QString _buffer;
-    Quassel::LogLevel _logLevel;
-};
+    /**
+     * Takes the given message with the given log level, formats it and emits the @a messageLogged() signal.
+     *
+     * @note This method is thread-safe.
+     *
+     * @param logLevel The log leve of the message
+     * @param message  The message
+     */
+    void handleMessage(LogLevel logLevel, const QString &message);
 
 
+signals:
+    /**
+     * Emitted whenever a message was logged.
+     *
+     * @param message The message that was logged
+     */
+    void messageLogged(const Logger::LogEntry &message);
 
 
-class quInfo : public Logger
-{
-public:
-    inline quInfo() : Logger(Quassel::InfoLevel) {}
-};
+private slots:
+    void onMessageLogged(const Logger::LogEntry &message);
 
 
+private:
+    void handleMessage(QtMsgType type, const QString &message);
+    void outputMessage(const LogEntry &message);
 
 
-class quWarning : public Logger
-{
-public:
-    inline quWarning() : Logger(Quassel::WarningLevel) {}
-};
-
+private:
+    LogLevel _outputLevel{LogLevel::Info};
+    QFile _logFile;
+    bool _syslogEnabled{false};
 
 
-class quError : public Logger
-{
-public:
-    inline quError() : Logger(Quassel::ErrorLevel) {}
+    std::vector<LogEntry> _messages;
+    bool _keepMessages{true};
+    bool _initialized{false};
 };
 
 };
 
-
-#endif
+Q_DECLARE_METATYPE(Logger::LogEntry)
diff --git a/src/common/logmessage.cpp b/src/common/logmessage.cpp
new file mode 100644 (file)
index 0000000..1715cfb
--- /dev/null
@@ -0,0 +1,52 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   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 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 "logmessage.h"
+
+#include <QFile>
+#include <QTextStream>
+#include <QDateTime>
+
+#include "quassel.h"
+
+LogMessage::LogMessage(Logger::LogLevel level)
+    : _stream(&_buffer, QIODevice::WriteOnly)
+    , _logLevel(level)
+{
+}
+
+
+LogMessage::~LogMessage()
+{
+    Quassel::instance()->logger()->handleMessage(_logLevel, _buffer);
+}
+
+
+LogMessage &LogMessage::operator<<(const QStringList &t)
+{
+    _stream << t.join(" ") << " ";
+    return *this;
+}
+
+
+LogMessage &LogMessage::operator<<(bool t) {
+    _stream << (t ? "true" : "false") << " ";
+    return *this;
+}
diff --git a/src/common/logmessage.h b/src/common/logmessage.h
new file mode 100644 (file)
index 0000000..e1e908f
--- /dev/null
@@ -0,0 +1,93 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   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 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.         *
+ ***************************************************************************/
+
+#pragma once
+
+#include <QStringList>
+#include <QTextStream>
+
+#include "logger.h"
+#include "types.h"
+
+/**
+ * Class encapsulating a single log message.
+ *
+ * Very similar in concept to qDebug() and friends.
+ */
+class LogMessage
+{
+public:
+    LogMessage(Logger::LogLevel level);
+    ~LogMessage();
+
+    template<typename T>
+    LogMessage &operator<<(const T &value) {
+        _stream << value << " ";
+        return *this;
+    }
+
+    LogMessage &operator<<(const QStringList &t);
+    LogMessage &operator<<(bool t);
+
+private:
+    QTextStream _stream;
+    QString _buffer;
+    Logger::LogLevel _logLevel;
+};
+
+
+// The only reason for LogMessage and the helpers below to exist is the fact that Qt versions
+// prior to 5.5 did not support the Info level.
+// Once we can rely on Qt 5.5, they will be removed and replaced by the native Qt functions.
+
+/**
+ * Creates an info-level log message.
+ *
+ * @sa qInfo
+ */
+class quInfo : public LogMessage
+{
+public:
+    quInfo() : LogMessage(Logger::LogLevel::Info) {}
+};
+
+
+/**
+ * Creates a warning-level log message.
+ *
+ * @sa qWarning
+ */
+class quWarning : public LogMessage
+{
+public:
+    quWarning() : LogMessage(Logger::LogLevel::Warning) {}
+};
+
+
+/**
+ * Creates an error-level log message.
+ *
+ * @sa qCritical
+ */
+class quError : public LogMessage
+{
+public:
+    quError() : LogMessage(Logger::LogLevel::Error) {}
+};
index 628c1a8..3076eea 100644 (file)
@@ -78,6 +78,9 @@ int main(int argc, char **argv)
     umask(S_IRWXG | S_IRWXO);
 #endif
 
     umask(S_IRWXG | S_IRWXO);
 #endif
 
+    // Instantiate early, so log messages are handled
+    Quassel::instance();
+
 #if QT_VERSION < 0x050000
     // All our source files are in UTF-8, and Qt5 even requires that
     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
 #if QT_VERSION < 0x050000
     // All our source files are in UTF-8, and Qt5 even requires that
     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
index 92b03e2..5ceddb1 100644 (file)
@@ -44,6 +44,7 @@
 #include "bufferinfo.h"
 #include "identity.h"
 #include "logger.h"
 #include "bufferinfo.h"
 #include "identity.h"
 #include "logger.h"
+#include "logmessage.h"
 #include "message.h"
 #include "network.h"
 #include "peer.h"
 #include "message.h"
 #include "network.h"
 #include "peer.h"
@@ -61,6 +62,7 @@ Quassel *Quassel::instance()
 
 
 Quassel::Quassel()
 
 
 Quassel::Quassel()
+    : _logger{new Logger{this}}
 {
 }
 
 {
 }
 
@@ -122,52 +124,19 @@ bool Quassel::init()
         return false;
     }
 
         return false;
     }
 
-    // Set up logging
-    if (isOptionSet("loglevel")) {
-        QString level = optionValue("loglevel").toLower();
-
-        if (level == "debug")
-            setLogLevel(DebugLevel);
-        else if (level == "info")
-            setLogLevel(InfoLevel);
-        else if (level == "warning")
-            setLogLevel(WarningLevel);
-        else if (level == "error")
-            setLogLevel(ErrorLevel);
-        else {
-            qWarning() << qPrintable(tr("Invalid log level %1; supported are Debug|Info|Warning|Error").arg(level));
-            return false;
-        }
-    }
-
-    QString logfilename = optionValue("logfile");
-    if (!logfilename.isEmpty()) {
-        instance()->_logFile.reset(new QFile{logfilename});
-        if (!logFile()->open(QIODevice::Append | QIODevice::Text)) {
-            qWarning() << qPrintable(tr("Could not open log file \"%1\": %2").arg(logfilename, logFile()->errorString()));
-            instance()->_logFile.reset();
-        }
-    }
-#ifdef HAVE_SYSLOG
-    instance()->_logToSyslog = isOptionSet("syslog");
-#endif
+    // Don't keep a debug log on the core
+    return instance()->logger()->setup(runMode() != RunMode::CoreOnly);
+}
 
 
-#if QT_VERSION < 0x050000
-    qInstallMsgHandler(Logger::logMessage);
-#else
-    qInstallMessageHandler(Logger::logMessage);
-#endif
 
 
-    return true;
+void Quassel::destroy()
+{
 }
 
 
 }
 
 
-void Quassel::destroy()
+Logger *Quassel::logger() const
 {
 {
-    if (logFile()) {
-        logFile()->close();
-        instance()->_logFile.reset();
-    }
+    return _logger;
 }
 
 
 }
 
 
@@ -429,45 +398,6 @@ bool Quassel::isOptionSet(const QString &key)
 }
 
 
 }
 
 
-Quassel::LogLevel Quassel::logLevel()
-{
-    return instance()->_logLevel;
-}
-
-
-void Quassel::setLogLevel(LogLevel logLevel)
-{
-    instance()->_logLevel = logLevel;
-}
-
-
-QFile *Quassel::logFile() {
-    return instance()->_logFile.get();
-}
-
-
-bool Quassel::logToSyslog()
-{
-    return instance()->_logToSyslog;
-}
-
-
-void Quassel::logFatalMessage(const char *msg)
-{
-#ifdef Q_OS_MAC
-    Q_UNUSED(msg)
-#else
-    QFile dumpFile(instance()->coreDumpFileName());
-    dumpFile.open(QIODevice::Append);
-    QTextStream dumpStream(&dumpFile);
-
-    dumpStream << "Fatal: " << msg << '\n';
-    dumpStream.flush();
-    dumpFile.close();
-#endif
-}
-
-
 const QString &Quassel::coreDumpFileName()
 {
     if (_coreDumpFileName.isEmpty()) {
 const QString &Quassel::coreDumpFileName()
 {
     if (_coreDumpFileName.isEmpty()) {
index 952f8fa..85660af 100644 (file)
@@ -35,6 +35,8 @@
 
 class QFile;
 
 
 class QFile;
 
+class Logger;
+
 class Quassel : public QObject
 {
     // TODO Qt5: Use Q_GADGET
 class Quassel : public QObject
 {
     // TODO Qt5: Use Q_GADGET
@@ -146,6 +148,13 @@ public:
 
     static Quassel *instance();
 
 
     static Quassel *instance();
 
+    /**
+     * Provides access to the Logger instance.
+     *
+     * @returns The Logger instance
+     */
+    Logger *logger() const;
+
     static void setupBuildInfo();
     static const BuildInfo &buildInfo();
     static RunMode runMode();
     static void setupBuildInfo();
     static const BuildInfo &buildInfo();
     static RunMode runMode();
@@ -182,20 +191,6 @@ public:
     static QString optionValue(const QString &option);
     static bool isOptionSet(const QString &option);
 
     static QString optionValue(const QString &option);
     static bool isOptionSet(const QString &option);
 
-    enum LogLevel {
-        DebugLevel,
-        InfoLevel,
-        WarningLevel,
-        ErrorLevel
-    };
-
-    static LogLevel logLevel();
-    static void setLogLevel(LogLevel logLevel);
-    static QFile *logFile();
-    static bool logToSyslog();
-
-    static void logFatalMessage(const char *msg);
-
     using ReloadHandler = std::function<bool()>;
 
     static void registerReloadHandler(ReloadHandler handler);
     using ReloadHandler = std::function<bool()>;
 
     static void registerReloadHandler(ReloadHandler handler);
@@ -204,6 +199,11 @@ public:
 
     static void registerQuitHandler(QuitHandler quitHandler);
 
 
     static void registerQuitHandler(QuitHandler quitHandler);
 
+    const QString &coreDumpFileName();
+
+signals:
+    void messageLogged(const QDateTime &timeStamp, const QString &msg);
+
 protected:
     static bool init();
     static void destroy();
 protected:
     static bool init();
     static void destroy();
@@ -223,8 +223,6 @@ private:
     void setupEnvironment();
     void registerMetaTypes();
 
     void setupEnvironment();
     void registerMetaTypes();
 
-    const QString &coreDumpFileName();
-
     /**
      * Requests a reload of relevant runtime configuration.
      *
     /**
      * Requests a reload of relevant runtime configuration.
      *
@@ -257,12 +255,10 @@ private:
     QStringList _dataDirPaths;
     QString _translationDirPath;
 
     QStringList _dataDirPaths;
     QString _translationDirPath;
 
-    LogLevel _logLevel{InfoLevel};
-    bool _logToSyslog{false};
-    std::unique_ptr<QFile> _logFile;
-
     std::shared_ptr<AbstractCliParser> _cliParser;
 
     std::shared_ptr<AbstractCliParser> _cliParser;
 
+    Logger *_logger;
+
     std::vector<ReloadHandler> _reloadHandlers;
     std::vector<QuitHandler> _quitHandlers;
 };
     std::vector<ReloadHandler> _reloadHandlers;
     std::vector<QuitHandler> _quitHandlers;
 };
index 89169ee..149aabb 100644 (file)
@@ -19,9 +19,6 @@
  ***************************************************************************/
 
 #include "abstractsqlstorage.h"
  ***************************************************************************/
 
 #include "abstractsqlstorage.h"
-#include "quassel.h"
-
-#include "logger.h"
 
 #include <QMutexLocker>
 #include <QSqlDriver>
 
 #include <QMutexLocker>
 #include <QSqlDriver>
@@ -29,6 +26,9 @@
 #include <QSqlField>
 #include <QSqlQuery>
 
 #include <QSqlField>
 #include <QSqlQuery>
 
+#include "logmessage.h"
+#include "quassel.h"
+
 int AbstractSqlStorage::_nextConnectionId = 0;
 AbstractSqlStorage::AbstractSqlStorage(QObject *parent)
     : Storage(parent),
 int AbstractSqlStorage::_nextConnectionId = 0;
 AbstractSqlStorage::AbstractSqlStorage(QObject *parent)
     : Storage(parent),
index a8232a3..9bfc787 100644 (file)
@@ -13,7 +13,7 @@
 */
 
 #include "cipher.h"
 */
 
 #include "cipher.h"
-#include "logger.h"
+#include "logmessage.h"
 
 Cipher::Cipher()
 {
 
 Cipher::Cipher()
 {
index d596344..5818e2f 100644 (file)
@@ -26,8 +26,8 @@
 #include "coreauthhandler.h"
 #include "coresession.h"
 #include "coresettings.h"
 #include "coreauthhandler.h"
 #include "coresession.h"
 #include "coresettings.h"
-#include "logger.h"
 #include "internalpeer.h"
 #include "internalpeer.h"
+#include "logmessage.h"
 #include "network.h"
 #include "postgresqlstorage.h"
 #include "quassel.h"
 #include "network.h"
 #include "postgresqlstorage.h"
 #include "quassel.h"
index 6cccaf8..240954e 100644 (file)
@@ -25,7 +25,7 @@
 #endif
 
 #include "core.h"
 #endif
 
 #include "core.h"
-#include "logger.h"
+#include "logmessage.h"
 
 using namespace Protocol;
 
 
 using namespace Protocol;
 
index 8c7539b..a7a67b7 100644 (file)
@@ -21,7 +21,7 @@
 #include "corebasichandler.h"
 
 #include "util.h"
 #include "corebasichandler.h"
 
 #include "util.h"
-#include "logger.h"
+#include "logmessage.h"
 
 CoreBasicHandler::CoreBasicHandler(CoreNetwork *parent)
     : BasicHandler(parent),
 
 CoreBasicHandler::CoreBasicHandler(CoreNetwork *parent)
     : BasicHandler(parent),
index 689f043..200fadc 100644 (file)
@@ -44,7 +44,7 @@
 #include "ircchannel.h"
 #include "ircparser.h"
 #include "ircuser.h"
 #include "ircchannel.h"
 #include "ircparser.h"
 #include "ircuser.h"
-#include "logger.h"
+#include "logmessage.h"
 #include "messageevent.h"
 #include "remotepeer.h"
 #include "storage.h"
 #include "messageevent.h"
 #include "remotepeer.h"
 #include "storage.h"
index 76cddb3..8fa7015 100644 (file)
@@ -28,7 +28,7 @@
 #include "ctcpevent.h"
 #include "ircevent.h"
 #include "ircuser.h"
 #include "ctcpevent.h"
 #include "ircevent.h"
 #include "ircuser.h"
-#include "logger.h"
+#include "logmessage.h"
 #include "messageevent.h"
 #include "netsplit.h"
 #include "quassel.h"
 #include "messageevent.h"
 #include "netsplit.h"
 #include "quassel.h"
index c3be8f5..5e72874 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "corenetwork.h"
 #include "identserver.h"
 
 #include "corenetwork.h"
 #include "identserver.h"
-#include "logger.h"
+#include "logmessage.h"
 
 IdentServer::IdentServer(QObject *parent)
     : QObject(parent)
 
 IdentServer::IdentServer(QObject *parent)
     : QObject(parent)
index 620ae51..b420440 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "ldapauthenticator.h"
 
 
 #include "ldapauthenticator.h"
 
-#include "logger.h"
+#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
 #include "network.h"
 #include "quassel.h"
 
index 16ab7c3..74d0b8f 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <QtSql>
 
 
 #include <QtSql>
 
-#include "logger.h"
+#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
 #include "network.h"
 #include "quassel.h"
 
index 57a6aa1..201fd95 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "sqlauthenticator.h"
 
 
 #include "sqlauthenticator.h"
 
-#include "logger.h"
+#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
 #include "network.h"
 #include "quassel.h"
 
index db174cb..1503015 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <QtSql>
 
 
 #include <QtSql>
 
-#include "logger.h"
+#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
 #include "network.h"
 #include "quassel.h"
 
index dd0792e..183bbe1 100644 (file)
@@ -26,8 +26,8 @@
 
 #include <QDateTime>
 
 
 #include <QDateTime>
 
-#include "logger.h"
 #include "quassel.h"
 #include "quassel.h"
+#include "logmessage.h"
 
 #ifdef HAVE_SSL
 
 
 #ifdef HAVE_SSL
 
index 975c6fd..83a5e09 100644 (file)
 
 #include "debuglogwidget.h"
 
 
 #include "debuglogwidget.h"
 
-#include "client.h"
+#include "quassel.h"
 
 DebugLogWidget::DebugLogWidget(QWidget *parent)
     : QWidget(parent)
 {
     ui.setupUi(this);
     setAttribute(Qt::WA_DeleteOnClose, true);
 
 DebugLogWidget::DebugLogWidget(QWidget *parent)
     : QWidget(parent)
 {
     ui.setupUi(this);
     setAttribute(Qt::WA_DeleteOnClose, true);
-    ui.textEdit->setPlainText(Client::debugLog());
-    connect(Client::instance(), SIGNAL(logUpdated(const QString &)), this, SLOT(logUpdated(const QString &)));
+
     ui.textEdit->setReadOnly(true);
     ui.textEdit->setReadOnly(true);
+
+    connect(Quassel::instance()->logger(), SIGNAL(messageLogged(Logger::LogEntry)), SLOT(logUpdated(Logger::LogEntry)));
+
+    QString content;
+    for (auto &&message : Quassel::instance()->logger()->messages()) {
+        content += toString(message);
+    }
+    ui.textEdit->setPlainText(content);
+
+}
+
+
+QString DebugLogWidget::toString(const Logger::LogEntry &msg)
+{
+    return msg.timeStamp.toString("yyyy-MM-dd hh:mm:ss ") + msg.message + "\n";
 }
 
 
 }
 
 
-void DebugLogWidget::logUpdated(const QString &msg)
+void DebugLogWidget::logUpdated(const Logger::LogEntry &msg)
 {
     ui.textEdit->moveCursor(QTextCursor::End);
 {
     ui.textEdit->moveCursor(QTextCursor::End);
-    ui.textEdit->insertPlainText(msg);
+    ui.textEdit->insertPlainText(toString(msg));
     ui.textEdit->moveCursor(QTextCursor::End);
 }
     ui.textEdit->moveCursor(QTextCursor::End);
 }
index 9a1fa8e..5bffad9 100644 (file)
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
-#ifndef DEBUGLOGWIDGET_H
-#define DEBUGLOGWIDGET_H
+#pragma once
+
+#include <QString>
+#include <QWidget>
+
+#include "logger.h"
 
 #include "ui_debuglogwidget.h"
 
 
 #include "ui_debuglogwidget.h"
 
@@ -31,11 +35,11 @@ public:
     DebugLogWidget(QWidget *parent = 0);
 
 private slots:
     DebugLogWidget(QWidget *parent = 0);
 
 private slots:
-    void logUpdated(const QString &msg);
+    void logUpdated(const Logger::LogEntry &msg);
+
+private:
+    QString toString(const Logger::LogEntry &msg);
 
 private:
     Ui::DebugLogWidget ui;
 };
 
 private:
     Ui::DebugLogWidget ui;
 };
-
-
-#endif //DEBUGLOGWIDGET_H
index fe1a0d0..99aaff3 100644 (file)
@@ -1974,7 +1974,7 @@ void MainWin::on_actionDebugMessageModel_triggered()
 
 void MainWin::on_actionDebugLog_triggered()
 {
 
 void MainWin::on_actionDebugLog_triggered()
 {
-    DebugLogWidget *logWidget = new DebugLogWidget(0);
+    DebugLogWidget *logWidget = new DebugLogWidget(nullptr);  // will be deleted on close
     logWidget->show();
 }
 
     logWidget->show();
 }
 
index 0e3cad7..907fb31 100644 (file)
@@ -24,7 +24,9 @@
 #include <tuple>
 #include <vector>
 
 #include <tuple>
 #include <vector>
 
-#include <QStringList>
+#include <QList>
+#include <QObject>
+#include <QString>
 
 #if QT_VERSION >= 0x050000
 #  include <QTemporaryDir>
 
 #if QT_VERSION >= 0x050000
 #  include <QTemporaryDir>
@@ -57,6 +59,8 @@ public:
     inline static QtUiStyle *style();
     inline static MainWin *mainWindow();
 
     inline static QtUiStyle *style();
     inline static MainWin *mainWindow();
 
+    QString debugLog() const;
+
     static bool haveSystemTray();
 
     /* Notifications */
     static bool haveSystemTray();
 
     /* Notifications */
index a16f66e..c9475fe 100644 (file)
@@ -87,10 +87,7 @@ QtUiApplication::QtUiApplication(int &argc, char **argv)
 
     Quassel::setRunMode(Quassel::ClientOnly);
 
 
     Quassel::setRunMode(Quassel::ClientOnly);
 
-#if QT_VERSION < 0x050000
-    qInstallMsgHandler(Client::logMessage);
-#else
-    qInstallMessageHandler(Client::logMessage);
+#if QT_VERSION >= 0x050000
     connect(this, &QGuiApplication::commitDataRequest, this, &QtUiApplication::commitData, Qt::DirectConnection);
     connect(this, &QGuiApplication::saveStateRequest, this, &QtUiApplication::saveState, Qt::DirectConnection);
 #endif
     connect(this, &QGuiApplication::commitDataRequest, this, &QtUiApplication::commitData, Qt::DirectConnection);
     connect(this, &QGuiApplication::saveStateRequest, this, &QtUiApplication::saveState, Qt::DirectConnection);
 #endif