diff --git a/chatterino.pro b/chatterino.pro index c70a48c42..e88ff6a8f 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -101,8 +101,8 @@ SOURCES += \ src/widgets/settingsdialog.cpp \ src/widgets/helper/settingsdialogtab.cpp \ src/widgets/textinputdialog.cpp \ - src/logging/loggingmanager.cpp \ - src/logging/loggingchannel.cpp \ + src/singletons/loggingmanager.cpp \ + src/singletons/helper/loggingchannel.cpp \ src/singletons/windowmanager.cpp \ src/singletons/channelmanager.cpp \ src/singletons/fontmanager.cpp \ @@ -193,8 +193,8 @@ HEADERS += \ src/widgets/helper/resizingtextedit.hpp \ src/messages/limitedqueue.hpp \ src/messages/limitedqueuesnapshot.hpp \ - src/logging/loggingmanager.hpp \ - src/logging/loggingchannel.hpp \ + src/singletons/loggingmanager.hpp \ + src/singletons/helper/loggingchannel.hpp \ src/singletons/channelmanager.hpp \ src/singletons/windowmanager.hpp \ src/singletons/settingsmanager.hpp \ diff --git a/src/application.cpp b/src/application.cpp index 0a6076ce6..4d282bb88 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,5 +1,5 @@ #include "application.hpp" -#include "logging/loggingmanager.hpp" +#include "singletons/loggingmanager.hpp" #include "singletons/accountmanager.hpp" #include "singletons/commandmanager.hpp" #include "singletons/emotemanager.hpp" @@ -18,7 +18,7 @@ Application::Application() { singletons::WindowManager::getInstance(); - logging::init(); + singletons::LoggingManager::getInstance(); singletons::SettingManager::getInstance().init(); singletons::CommandManager::getInstance().loadCommands(); diff --git a/src/channel.cpp b/src/channel.cpp index 7acf19d79..03658b0b4 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -1,9 +1,9 @@ #include "channel.hpp" #include "debug/log.hpp" -#include "logging/loggingmanager.hpp" #include "messages/message.hpp" #include "singletons/emotemanager.hpp" #include "singletons/ircmanager.hpp" +#include "singletons/loggingmanager.hpp" #include "singletons/windowmanager.hpp" #include @@ -20,7 +20,6 @@ namespace chatterino { Channel::Channel(const QString &_name) : name(_name) -// , loggingChannel(logging::get(name)) { } @@ -45,12 +44,10 @@ void Channel::addMessage(MessagePtr message) this->addRecentChatter(message); } - // if (_loggingChannel.get() != nullptr) { - // _loggingChannel->append(message); - // } + singletons::LoggingManager::getInstance().addMessage(this->name, message); if (this->messages.pushBack(message, deleted)) { - messageRemovedFromStart(deleted); + this->messageRemovedFromStart(deleted); } this->messageAppended(message); diff --git a/src/channel.hpp b/src/channel.hpp index f6a80d9ef..7724560f2 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -1,8 +1,8 @@ #pragma once -#include "logging/loggingchannel.hpp" #include "messages/image.hpp" #include "messages/limitedqueue.hpp" +#include "messages/message.hpp" #include "util/concurrentmap.hpp" #include @@ -60,8 +60,6 @@ public: private: messages::LimitedQueue messages; - - // std::shared_ptr loggingChannel; }; typedef std::shared_ptr ChannelPtr; diff --git a/src/logging/loggingmanager.cpp b/src/logging/loggingmanager.cpp deleted file mode 100644 index 65bc27cfc..000000000 --- a/src/logging/loggingmanager.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "loggingmanager.hpp" - -#include -#include - -#include - -namespace chatterino { -namespace logging { - -static QString logBasePath; -static QString channelBasePath; -static QString whispersBasePath; -static QString mentionsBasePath; - -std::unordered_map> channels; - -void init() -{ - // Make sure all folders are properly created - logBasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + - QDir::separator() + "Logs"; - channelBasePath = logBasePath + QDir::separator() + "Channels"; - whispersBasePath = logBasePath + QDir::separator() + "Whispers"; - mentionsBasePath = logBasePath + QDir::separator() + "Mentions"; - - { - QDir dir(logBasePath); - if (!dir.exists()) { - dir.mkpath("."); - } - } - - { - QDir dir(channelBasePath); - if (!dir.exists()) { - dir.mkpath("."); - } - } - - { - QDir dir(whispersBasePath); - if (!dir.exists()) { - dir.mkpath("."); - } - } - - { - QDir dir(mentionsBasePath); - if (!dir.exists()) { - dir.mkpath("."); - } - } -} - -static const QString &getBaseDirectory(const QString &channelName) -{ - if (channelName == "/whispers") { - return whispersBasePath; - } else if (channelName == "/mentions") { - return mentionsBasePath; - } else { - return channelBasePath; - } -} - -std::shared_ptr get(const QString &channelName) -{ - if (channelName.isEmpty()) { - return nullptr; - } - - const QString &baseDirectory = getBaseDirectory(channelName); - - auto channel = channels.find(channelName.toStdString()); - if (channel == std::end(channels)) { - // This channel is definitely not logged yet. - // Create shared_ptr that we will return, and store a weak_ptr reference - std::shared_ptr ret = - std::shared_ptr(new Channel(channelName, baseDirectory)); - - channels[channelName.toStdString()] = std::weak_ptr(ret); - - return ret; - } - - if (auto ret = channels[channelName.toStdString()].lock()) { - return ret; - } - - std::shared_ptr ret = - std::shared_ptr(new Channel(channelName, baseDirectory)); - - channels[channelName.toStdString()] = std::weak_ptr(ret); - - return ret; -} - -} // namespace logging -} // namespace chatterino diff --git a/src/logging/loggingmanager.hpp b/src/logging/loggingmanager.hpp deleted file mode 100644 index 9080a0dd9..000000000 --- a/src/logging/loggingmanager.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "loggingchannel.hpp" - -#include - -namespace chatterino { -namespace logging { - -void init(); -std::shared_ptr get(const QString &channelName); - -} // namespace logging -} // namespace chatterino diff --git a/src/logging/loggingchannel.cpp b/src/singletons/helper/loggingchannel.cpp similarity index 58% rename from src/logging/loggingchannel.cpp rename to src/singletons/helper/loggingchannel.cpp index 249fbd5d5..e5454716d 100644 --- a/src/logging/loggingchannel.cpp +++ b/src/singletons/helper/loggingchannel.cpp @@ -1,35 +1,34 @@ #include "loggingchannel.hpp" -#include "loggingmanager.hpp" #include #include namespace chatterino { -namespace logging { +namespace singletons { -Channel::Channel(const QString &_channelName, const QString &_baseDirectory) +LoggingChannel::LoggingChannel(const QString &_channelName, const QString &_baseDirectory) : channelName(_channelName) , baseDirectory(_baseDirectory) { QDateTime now = QDateTime::currentDateTime(); - this->fileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log"; + QString baseFileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log"; // Open file handle to log file of current date - this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileName); + this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + baseFileName); this->fileHandle.open(QIODevice::Append); this->appendLine(this->generateOpeningString(now)); } -Channel::~Channel() +LoggingChannel::~LoggingChannel() { this->appendLine(this->generateClosingString()); this->fileHandle.close(); } -void Channel::append(std::shared_ptr message) +void LoggingChannel::addMessage(std::shared_ptr message) { QDateTime now = QDateTime::currentDateTime(); @@ -37,14 +36,21 @@ void Channel::append(std::shared_ptr message) str.append('['); str.append(now.toString("HH:mm:ss")); str.append("] "); - str.append(message->loginName); - str.append(": "); - str.append(message->searchText); - str.append('\n'); + + if ((message->flags & messages::Message::MessageFlags::System) != 0) { + str.append(message->searchText); + str.append('\n'); + } else { + str.append(message->loginName); + str.append(": "); + str.append(message->searchText); + str.append('\n'); + } + this->appendLine(str); } -QString Channel::generateOpeningString(const QDateTime &now) const +QString LoggingChannel::generateOpeningString(const QDateTime &now) const { QString ret = QLatin1Literal("# Start logging at "); @@ -55,7 +61,7 @@ QString Channel::generateOpeningString(const QDateTime &now) const return ret; } -QString Channel::generateClosingString(const QDateTime &now) const +QString LoggingChannel::generateClosingString(const QDateTime &now) const { QString ret = QLatin1Literal("# Stop logging at "); @@ -66,11 +72,11 @@ QString Channel::generateClosingString(const QDateTime &now) const return ret; } -void Channel::appendLine(const QString &line) +void LoggingChannel::appendLine(const QString &line) { this->fileHandle.write(line.toUtf8()); this->fileHandle.flush(); } -} // namespace logging +} // namespace singletons } // namespace chatterino diff --git a/src/logging/loggingchannel.hpp b/src/singletons/helper/loggingchannel.hpp similarity index 61% rename from src/logging/loggingchannel.hpp rename to src/singletons/helper/loggingchannel.hpp index 836015f7c..b60fb3540 100644 --- a/src/logging/loggingchannel.hpp +++ b/src/singletons/helper/loggingchannel.hpp @@ -5,19 +5,20 @@ #include #include #include +#include #include namespace chatterino { -namespace logging { +namespace singletons { -class Channel +class LoggingChannel : boost::noncopyable { -public: - explicit Channel(const QString &_channelName, const QString &_baseDirectory); - ~Channel(); + explicit LoggingChannel(const QString &_channelName, const QString &_baseDirectory); - void append(std::shared_ptr message); +public: + ~LoggingChannel(); + void addMessage(std::shared_ptr message); private: QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const; @@ -30,7 +31,9 @@ private: const QString &baseDirectory; QString fileName; QFile fileHandle; + + friend class LoggingManager; }; -} // namespace logging +} // namespace singletons } // namespace chatterino diff --git a/src/singletons/loggingmanager.cpp b/src/singletons/loggingmanager.cpp new file mode 100644 index 000000000..432f8f8a4 --- /dev/null +++ b/src/singletons/loggingmanager.cpp @@ -0,0 +1,61 @@ +#include "singletons/loggingmanager.hpp" +#include "debug/log.hpp" +#include "singletons/pathmanager.hpp" +#include "singletons/settingsmanager.hpp" + +#include +#include + +#include + +namespace chatterino { +namespace singletons { + +LoggingManager::LoggingManager() + : pathManager(PathManager::getInstance()) +{ +} + +LoggingManager &LoggingManager::getInstance() +{ + static LoggingManager instance; + return instance; +} + +void LoggingManager::addMessage(const QString &channelName, messages::MessagePtr message) +{ + const auto &settings = singletons::SettingManager::getInstance(); + + if (!settings.enableLogging) { + return; + } + + auto it = this->loggingChannels.find(channelName); + if (it == this->loggingChannels.end()) { + auto channel = new LoggingChannel(channelName, this->getDirectoryForChannel(channelName)); + channel->addMessage(message); + this->loggingChannels.emplace(channelName, std::move(channel)); + } else { + it->second->addMessage(message); + } +} + +QString LoggingManager::getDirectoryForChannel(const QString &channelName) +{ + if (channelName.startsWith("/whispers")) { + return this->pathManager.whispersLogsFolderPath; + } else if (channelName.startsWith("/mentions")) { + return this->pathManager.mentionsLogsFolderPath; + } else { + QString logPath(this->pathManager.channelsLogsFolderPath + QDir::separator() + channelName); + + if (!this->pathManager.createFolder(logPath)) { + debug::Log("Error creating channel logs folder for channel {}", channelName); + } + + return logPath; + } +} + +} // namespace singletons +} // namespace chatterino diff --git a/src/singletons/loggingmanager.hpp b/src/singletons/loggingmanager.hpp new file mode 100644 index 000000000..8371db7e6 --- /dev/null +++ b/src/singletons/loggingmanager.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "messages/message.hpp" +#include "singletons/helper/loggingchannel.hpp" + +#include + +#include + +namespace chatterino { +namespace singletons { + +class PathManager; + +class LoggingManager : boost::noncopyable +{ + LoggingManager(); + + PathManager &pathManager; + +public: + static LoggingManager &getInstance(); + + void addMessage(const QString &channelName, messages::MessagePtr message); + +private: + std::map> loggingChannels; + QString getDirectoryForChannel(const QString &channelName); +}; + +} // namespace singletons +} // namespace chatterino diff --git a/src/singletons/pathmanager.cpp b/src/singletons/pathmanager.cpp index 0047b11e6..a6356b208 100644 --- a/src/singletons/pathmanager.cpp +++ b/src/singletons/pathmanager.cpp @@ -57,8 +57,44 @@ bool PathManager::init(int argc, char **argv) return false; } + this->logsFolderPath = rootPath + "/Logs"; + + if (!QDir().mkpath(this->logsFolderPath)) { + printf("Error creating logs directory: %s\n", qPrintable(this->logsFolderPath)); + return false; + } + + this->channelsLogsFolderPath = this->logsFolderPath + "/Channels"; + + if (!QDir().mkpath(this->channelsLogsFolderPath)) { + printf("Error creating channelsLogs directory: %s\n", + qPrintable(this->channelsLogsFolderPath)); + return false; + } + + this->whispersLogsFolderPath = this->logsFolderPath + "/Whispers"; + + if (!QDir().mkpath(this->whispersLogsFolderPath)) { + printf("Error creating whispersLogs directory: %s\n", + qPrintable(this->whispersLogsFolderPath)); + return false; + } + + this->mentionsLogsFolderPath = this->logsFolderPath + "/Mentions"; + + if (!QDir().mkpath(this->mentionsLogsFolderPath)) { + printf("Error creating mentionsLogs directory: %s\n", + qPrintable(this->mentionsLogsFolderPath)); + return false; + } + return true; } +bool PathManager::createFolder(const QString &folderPath) +{ + return QDir().mkpath(folderPath); +} + } // namespace singletons } // namespace chatterino diff --git a/src/singletons/pathmanager.hpp b/src/singletons/pathmanager.hpp index 7d89af9bc..f2d3e83ef 100644 --- a/src/singletons/pathmanager.hpp +++ b/src/singletons/pathmanager.hpp @@ -17,6 +17,14 @@ public: QString settingsFolderPath; QString customFolderPath; QString cacheFolderPath; + + // Logs + QString logsFolderPath; + QString channelsLogsFolderPath; + QString whispersLogsFolderPath; + QString mentionsLogsFolderPath; + + bool createFolder(const QString &folderPath); }; } // namespace singletons diff --git a/src/singletons/settingsmanager.hpp b/src/singletons/settingsmanager.hpp index 986115a29..ade6377ae 100644 --- a/src/singletons/settingsmanager.hpp +++ b/src/singletons/settingsmanager.hpp @@ -89,6 +89,9 @@ public: BoolSetting enableHighlightTaskbar = {"/highlighting/enableTaskbarFlashing", true}; BoolSetting customHighlightSound = {"/highlighting/useCustomSound", false}; + /// Logging + BoolSetting enableLogging = {"/logging/enabled", false}; + ChatterinoSetting> highlightProperties = { "/highlighting/highlights"}; diff --git a/src/widgets/settingspages/logspage.cpp b/src/widgets/settingspages/logspage.cpp index 62adefc5b..e99327e19 100644 --- a/src/widgets/settingspages/logspage.cpp +++ b/src/widgets/settingspages/logspage.cpp @@ -1,12 +1,32 @@ #include "logspage.hpp" +#include +#include + +#include "util/layoutcreator.hpp" + namespace chatterino { namespace widgets { namespace settingspages { + LogsPage::LogsPage() : SettingsPage("Logs", "") { + singletons::SettingManager &settings = singletons::SettingManager::getInstance(); + util::LayoutCreator layoutCreator(this); + auto layout = layoutCreator.emplace().withoutMargin(); + + { + auto form = layout.emplace(); + + // clang-format off + form->addRow("Enabled:", this->createCheckBox("Enable logging", settings.enableLogging)); + // clang-format on + } + + layout->addStretch(1); } + } // namespace settingspages } // namespace widgets } // namespace chatterino