From 1058572c8a53b4a6caf10588f50e4a1afcf273a5 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Mon, 25 Jan 2016 01:06:06 +0200 Subject: [PATCH] Implement file logger. --- src/app/app.pri | 12 ++- src/app/application.cpp | 14 ++++ src/app/application.h | 6 ++ src/app/filelogger.cpp | 174 +++++++++++++++++++++++++++++++++++++++ src/app/filelogger.h | 68 +++++++++++++++ src/base/preferences.cpp | 84 +++++++++++++++++++ src/base/preferences.h | 17 ++++ src/gui/options.ui | 151 ++++++++++++++++++++++++++++++++- src/gui/options_imp.cpp | 45 ++++++++++ src/gui/options_imp.h | 1 + 10 files changed, 566 insertions(+), 6 deletions(-) create mode 100644 src/app/filelogger.cpp create mode 100644 src/app/filelogger.h diff --git a/src/app/app.pri b/src/app/app.pri index cc56e0474..cdf5b4b52 100644 --- a/src/app/app.pri +++ b/src/app/app.pri @@ -14,8 +14,14 @@ usesystemqtsingleapplication { } } -HEADERS += $$PWD/application.h -SOURCES += $$PWD/application.cpp +HEADERS += \ + $$PWD/application.h \ + $$PWD/filelogger.h + +SOURCES += \ + $$PWD/application.cpp \ + $$PWD/filelogger.cpp \ + $$PWD/main.cpp unix: HEADERS += $$PWD/stacktrace.h strace_win { @@ -26,7 +32,5 @@ strace_win { } } -SOURCES += $$PWD/main.cpp - # upgrade code HEADERS += $$PWD/upgrade.h diff --git a/src/app/application.cpp b/src/app/application.cpp index ae2be596b..392c77cc1 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -58,6 +58,7 @@ #endif #include "application.h" +#include "filelogger.h" #include "base/logger.h" #include "base/settingsstorage.h" #include "base/preferences.h" @@ -105,10 +106,22 @@ Application::Application(const QString &id, int &argc, char **argv) connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &))); connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup())); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); + configure(); Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION)); } +void Application::configure() +{ + bool fileLogEnabled = Preferences::instance()->fileLogEnabled(); + + if (fileLogEnabled && !m_fileLogger) + m_fileLogger = new FileLogger; + else if (!fileLogEnabled) + delete m_fileLogger; +} + void Application::processMessage(const QString &message) { QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts); @@ -469,6 +482,7 @@ void Application::cleanup() Net::DownloadManager::freeInstance(); Preferences::freeInstance(); SettingsStorage::freeInstance(); + delete m_fileLogger; Logger::freeInstance(); IconProvider::freeInstance(); #ifndef DISABLE_GUI diff --git a/src/app/application.h b/src/app/application.h index 0f094e535..b00e18cc9 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -56,6 +56,8 @@ typedef QtSingleCoreApplication BaseApplication; class WebUI; #endif +class FileLogger; + namespace BitTorrent { class TorrentHandle; @@ -83,6 +85,7 @@ protected: #endif private slots: + void configure(); void processMessage(const QString &message); void torrentFinished(BitTorrent::TorrentHandle *const torrent); void allTorrentsFinished(); @@ -103,6 +106,9 @@ private: QPointer m_webui; #endif + // FileLog + QPointer m_fileLogger; + QTranslator m_qtTranslator; QTranslator m_translator; QStringList m_paramsQueue; diff --git a/src/app/filelogger.cpp b/src/app/filelogger.cpp new file mode 100644 index 000000000..b055076ea --- /dev/null +++ b/src/app/filelogger.cpp @@ -0,0 +1,174 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 sledgehammer999 + * + * 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) 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 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. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include +#include "filelogger.h" +#include "base/preferences.h" +#include "base/logger.h" +#include "base/utils/fs.h" + +namespace +{ + enum FileLogAgeType + { + DAYS, + MONTHS, + YEARS + }; +} + +FileLogger::FileLogger() + : m_logFile(nullptr) +{ + m_flusher.setInterval(0); + m_flusher.setSingleShot(true); + connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog())); + + configure(); + const Logger* const logger = Logger::instance(); + foreach (const Log::Msg& msg, logger->getMessages()) + addLogMessage(msg); + + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); + connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &))); +} + +FileLogger::~FileLogger() +{ + if (!m_logFile) return; + closeLogFile(); + delete m_logFile; +} + +void FileLogger::configure() +{ + const Preferences* const pref = Preferences::instance(); + QString tmpPath = Utils::Fs::fromNativePath(pref->fileLogPath()); + QDir dir(tmpPath); + dir.mkpath(tmpPath); + tmpPath = dir.absoluteFilePath("qbittorrent.log"); + + if (tmpPath != m_path) { + m_path = tmpPath; + + if (m_logFile) { + closeLogFile(); + delete m_logFile; + } + m_logFile = new QFile(m_path); + openLogFile(); + } + + m_backup = pref->fileLogBackup(); + m_size = pref->fileLogMaxSize(); + + if (pref->fileLogDeleteOld()) { + QDateTime date = QDateTime::currentDateTime(); + + switch (static_cast(pref->fileLogAgeType())) { + case DAYS: + date = date.addDays(pref->fileLogAge()); + break; + case MONTHS: + date = date.addMonths(pref->fileLogAge()); + break; + default: + date = date.addYears(pref->fileLogAge()); + } + + foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) { + if (file.lastModified() < date) + break; + Utils::Fs::forceRemove(file.absoluteFilePath()); + } + } +} + +void FileLogger::addLogMessage(const Log::Msg &msg) +{ + if (!m_logFile) return; + + QTextStream str(m_logFile); + + switch (msg.type) { + case Log::INFO: + str << "(I) "; + break; + case Log::WARNING: + str << "(W) "; + break; + case Log::CRITICAL: + str << "(C) "; + break; + default: + str << "(N) "; + } + + str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl; + + if (m_backup && (m_logFile->size() >= (m_size * 1024 * 1024))) { + closeLogFile(); + int counter = 0; + QString backupLogFilename = m_path + ".bak"; + + while (QFile::exists(backupLogFilename)) { + ++counter; + backupLogFilename = m_path + ".bak" + QString::number(counter); + } + + QFile::rename(m_path, backupLogFilename); + openLogFile(); + } + else { + m_flusher.start(); + } +} + +void FileLogger::flushLog() +{ + if (m_logFile) + m_logFile->flush(); +} + +void FileLogger::openLogFile() +{ + if (!m_logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text) + || !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) { + delete m_logFile; + m_logFile = nullptr; + Logger::instance()->addMessage(tr("An error occured while trying to open the log file. Logging to file is disabled."), Log::CRITICAL); + } +} + +void FileLogger::closeLogFile() +{ + m_flusher.stop(); + m_logFile->close(); +} diff --git a/src/app/filelogger.h b/src/app/filelogger.h new file mode 100644 index 000000000..2569aa35d --- /dev/null +++ b/src/app/filelogger.h @@ -0,0 +1,68 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 sledgehammer999 + * + * 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) 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 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. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef FILELOGGER_H +#define FILELOGGER_H + +#include +#include + +class QFile; + +namespace Log +{ + struct Msg; +} + +class FileLogger : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(FileLogger) + +public: + FileLogger(); + ~FileLogger(); + +private slots: + void configure(); + void addLogMessage(const Log::Msg &msg); + void flushLog(); + +private: + void openLogFile(); + void closeLogFile(); + + bool m_backup; + int m_size; + QString m_path; + QFile *m_logFile; + QTimer m_flusher; +}; + +#endif // FILELOGGER_H + diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 8bf30cfe7..4cf32d5cb 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -60,6 +60,8 @@ Preferences* Preferences::m_instance = 0; +static const QString LOG_FOLDER("logs"); + Preferences::Preferences() : m_randomPort(rand() % 64512 + 1024) { @@ -883,6 +885,88 @@ void Preferences::setExecutionLogMessageTypes(const int &value) setValue("MainWindow/ExecutionLog/Types", value); } +// File log +bool Preferences::fileLogEnabled() const +{ + return value("Application/FileLogger/Enabled", true).toBool(); +} + +void Preferences::setFileLogEnabled(bool enabled) +{ + setValue("Application/FileLogger/Enabled", enabled); +} + +QString Preferences::fileLogPath() const +{ + return value("Application/FileLogger/Path", QVariant(Utils::Fs::QDesktopServicesDataLocation() + LOG_FOLDER)).toString(); +} + +void Preferences::setFileLogPath(const QString &path) +{ + setValue("Application/FileLogger/Path", path); +} + +bool Preferences::fileLogBackup() const +{ + return value("Application/FileLogger/Backup", true).toBool(); +} + +void Preferences::setFileLogBackup(bool backup) +{ + setValue("Application/FileLogger/Backup", backup); +} + +bool Preferences::fileLogDeleteOld() const +{ + return value("Application/FileLogger/DeleteOld", true).toBool(); +} + +void Preferences::setFileLogDeleteOld(bool deleteOld) +{ + setValue("Application/FileLogger/DeleteOld", deleteOld); +} + +int Preferences::fileLogMaxSize() const +{ + int val = value("Application/FileLogger/MaxSize", 10).toInt(); + if (val < 1) + return 1; + if (val > 1000) + return 1000; + return val; +} + +void Preferences::setFileLogMaxSize(const int &size) +{ + setValue("Application/FileLogger/MaxSize", std::min(std::max(size, 1), 1000)); +} + +int Preferences::fileLogAge() const +{ + int val = value("Application/FileLogger/Age", 6).toInt(); + if (val < 1) + return 1; + if (val > 365) + return 365; + return val; +} + +void Preferences::setFileLogAge(const int &age) +{ + setValue("Application/FileLogger/Age", std::min(std::max(age, 1), 365)); +} + +int Preferences::fileLogAgeType() const +{ + int val = value("Application/FileLogger/AgeType", 1).toInt(); + return (val < 0 || val > 2) ? 1 : val; +} + +void Preferences::setFileLogAgeType(const int &ageType) +{ + setValue("Application/FileLogger/AgeType", (ageType < 0 || ageType > 2) ? 1 : ageType); +} + // Queueing system bool Preferences::isQueueingSystemEnabled() const { diff --git a/src/base/preferences.h b/src/base/preferences.h index b75a4e6f6..0b44c333d 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -279,6 +279,23 @@ public: int executionLogMessageTypes() const; void setExecutionLogMessageTypes(const int &value); + // File log + bool fileLogEnabled() const; + void setFileLogEnabled(bool enabled); + QString fileLogPath() const; + void setFileLogPath(const QString &path); + bool fileLogBackup() const; + void setFileLogBackup(bool backup); + bool fileLogDeleteOld() const; + void setFileLogDeleteOld(bool deleteOld); + int fileLogMaxSize() const; + void setFileLogMaxSize(const int &size); + int fileLogAge() const; + void setFileLogAge(const int &age); + int fileLogAgeType() const; + void setFileLogAgeType(const int &ageType); + + // Queueing system bool isQueueingSystemEnabled() const; void setQueueingSystemEnabled(bool enabled); diff --git a/src/gui/options.ui b/src/gui/options.ui index d7a0c105a..ac06db522 100644 --- a/src/gui/options.ui +++ b/src/gui/options.ui @@ -113,9 +113,9 @@ 0 - 0 + -190 486 - 702 + 732 @@ -470,6 +470,153 @@ + + + + Log file + + + true + + + true + + + + + + + + Save path: + + + + + + + + + + + 0 + 25 + + + + ... + + + + + + + + + 0 + + + + + Backup the log file after: + + + + + + + MB + + + 1 + + + 1000 + + + 10 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Delete backup logs older than: + + + + + + + 1 + + + 365 + + + 6 + + + + + + + 1 + + + + days + + + + + months + + + + + years + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index 3293ded57..30aa0cbec 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -167,6 +167,16 @@ options_imp::options_imp(QWidget *parent) connect(checkAssociateTorrents, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkAssociateMagnetLinks, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); #endif + connect(checkFileLog, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(textFileLogPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); + connect(checkFileLogBackup, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(checkFileLogBackup, SIGNAL(toggled(bool)), spinFileLogSize, SLOT(setEnabled(bool))); + connect(checkFileLogDelete, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(checkFileLogDelete, SIGNAL(toggled(bool)), spinFileLogAge, SLOT(setEnabled(bool))); + connect(checkFileLogDelete, SIGNAL(toggled(bool)), comboFileLogAgeType, SLOT(setEnabled(bool))); + connect(spinFileLogSize, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton())); + connect(spinFileLogAge, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton())); + connect(comboFileLogAgeType, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); // Downloads tab connect(textSavePath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(radioBtnEnableSubcategories, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); @@ -434,6 +444,13 @@ void options_imp::saveOptions() checkAssociateMagnetLinks->setEnabled(!checkAssociateMagnetLinks->isChecked()); } #endif + pref->setFileLogEnabled(checkFileLog->isChecked()); + pref->setFileLogPath(Utils::Fs::fromNativePath(textFileLogPath->text())); + pref->setFileLogBackup(checkFileLogBackup->isChecked()); + pref->setFileLogMaxSize(spinFileLogSize->value()); + pref->setFileLogDeleteOld(checkFileLogDelete->isChecked()); + pref->setFileLogAge(spinFileLogAge->value()); + pref->setFileLogAgeType(comboFileLogAgeType->currentIndex()); // End General preferences auto session = BitTorrent::Session::instance(); @@ -588,6 +605,8 @@ void options_imp::loadOptions() int intValue; qreal floatValue; QString strValue; + bool fileLogBackup = true; + bool fileLogDelete = true; const Preferences* const pref = Preferences::instance(); // General preferences @@ -621,6 +640,19 @@ void options_imp::loadOptions() checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet()); checkAssociateMagnetLinks->setEnabled(!checkAssociateMagnetLinks->isChecked()); #endif + + checkFileLog->setChecked(pref->fileLogEnabled()); + textFileLogPath->setText(Utils::Fs::toNativePath(pref->fileLogPath())); + fileLogBackup = pref->fileLogBackup(); + checkFileLogBackup->setChecked(fileLogBackup); + spinFileLogSize->setEnabled(fileLogBackup); + fileLogDelete = pref->fileLogDeleteOld(); + checkFileLogDelete->setChecked(fileLogDelete); + spinFileLogAge->setEnabled(fileLogDelete); + comboFileLogAgeType->setEnabled(fileLogDelete); + spinFileLogSize->setValue(pref->fileLogMaxSize()); + spinFileLogAge->setValue(pref->fileLogAge()); + comboFileLogAgeType->setCurrentIndex(pref->fileLogAgeType()); // End General preferences auto session = BitTorrent::Session::instance(); @@ -1293,6 +1325,19 @@ QString options_imp::askForExportDir(const QString& currentExportPath) return dir; } +void options_imp::on_browseFileLogDir_clicked() +{ + const QString path = Utils::Fs::expandPathAbs(Utils::Fs::fromNativePath(textFileLogPath->text())); + QDir pathDir(path); + QString dir; + if (!path.isEmpty() && pathDir.exists()) + dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), pathDir.absolutePath()); + else + dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), QDir::homePath()); + if (!dir.isNull()) + textFileLogPath->setText(Utils::Fs::toNativePath(dir)); +} + void options_imp::on_browseExportDirButton_clicked() { const QString newExportDir = askForExportDir(textExportDir->text()); diff --git a/src/gui/options_imp.h b/src/gui/options_imp.h index 6270cd6a6..1f89fb82a 100644 --- a/src/gui/options_imp.h +++ b/src/gui/options_imp.h @@ -83,6 +83,7 @@ private slots: void handleScanFolderViewSelectionChanged(); void on_IpFilterRefreshBtn_clicked(); void handleIPFilterParsed(bool error, int ruleCount); + void on_browseFileLogDir_clicked(); void on_browseExportDirButton_clicked(); void on_browseExportDirFinButton_clicked(); void on_browseFilterButton_clicked();