Basic logging implemented

Implemented scuffed logging settings page
Add helper function to PathManager to create an arbitrary folder
This commit is contained in:
Rasmus Karlsson 2018-01-28 14:23:55 +01:00
parent f474db9443
commit 2f0844ebd9
14 changed files with 201 additions and 151 deletions

View file

@ -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 \

View file

@ -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();

View file

@ -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 <QJsonArray>
@ -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);

View file

@ -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 <QMap>
@ -60,8 +60,6 @@ public:
private:
messages::LimitedQueue<messages::MessagePtr> messages;
// std::shared_ptr<logging::Channel> loggingChannel;
};
typedef std::shared_ptr<Channel> ChannelPtr;

View file

@ -1,100 +0,0 @@
#include "loggingmanager.hpp"
#include <QDir>
#include <QStandardPaths>
#include <unordered_map>
namespace chatterino {
namespace logging {
static QString logBasePath;
static QString channelBasePath;
static QString whispersBasePath;
static QString mentionsBasePath;
std::unordered_map<std::string, std::weak_ptr<Channel>> 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<Channel> 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<Channel> ret =
std::shared_ptr<Channel>(new Channel(channelName, baseDirectory));
channels[channelName.toStdString()] = std::weak_ptr<Channel>(ret);
return ret;
}
if (auto ret = channels[channelName.toStdString()].lock()) {
return ret;
}
std::shared_ptr<Channel> ret =
std::shared_ptr<Channel>(new Channel(channelName, baseDirectory));
channels[channelName.toStdString()] = std::weak_ptr<Channel>(ret);
return ret;
}
} // namespace logging
} // namespace chatterino

View file

@ -1,14 +0,0 @@
#pragma once
#include "loggingchannel.hpp"
#include <memory>
namespace chatterino {
namespace logging {
void init();
std::shared_ptr<Channel> get(const QString &channelName);
} // namespace logging
} // namespace chatterino

View file

@ -1,35 +1,34 @@
#include "loggingchannel.hpp"
#include "loggingmanager.hpp"
#include <QDir>
#include <ctime>
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<messages::Message> message)
void LoggingChannel::addMessage(std::shared_ptr<messages::Message> message)
{
QDateTime now = QDateTime::currentDateTime();
@ -37,14 +36,21 @@ void Channel::append(std::shared_ptr<messages::Message> 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

View file

@ -5,19 +5,20 @@
#include <QDateTime>
#include <QFile>
#include <QString>
#include <boost/noncopyable.hpp>
#include <memory>
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<messages::Message> message);
public:
~LoggingChannel();
void addMessage(std::shared_ptr<messages::Message> 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

View file

@ -0,0 +1,61 @@
#include "singletons/loggingmanager.hpp"
#include "debug/log.hpp"
#include "singletons/pathmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include <QDir>
#include <QStandardPaths>
#include <unordered_map>
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

View file

@ -0,0 +1,32 @@
#pragma once
#include "messages/message.hpp"
#include "singletons/helper/loggingchannel.hpp"
#include <boost/noncopyable.hpp>
#include <memory>
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<QString, std::unique_ptr<LoggingChannel>> loggingChannels;
QString getDirectoryForChannel(const QString &channelName);
};
} // namespace singletons
} // namespace chatterino

View file

@ -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

View file

@ -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

View file

@ -89,6 +89,9 @@ public:
BoolSetting enableHighlightTaskbar = {"/highlighting/enableTaskbarFlashing", true};
BoolSetting customHighlightSound = {"/highlighting/useCustomSound", false};
/// Logging
BoolSetting enableLogging = {"/logging/enabled", false};
ChatterinoSetting<std::vector<messages::HighlightPhrase>> highlightProperties = {
"/highlighting/highlights"};

View file

@ -1,12 +1,32 @@
#include "logspage.hpp"
#include <QFormLayout>
#include <QVBoxLayout>
#include "util/layoutcreator.hpp"
namespace chatterino {
namespace widgets {
namespace settingspages {
LogsPage::LogsPage()
: SettingsPage("Logs", "")
{
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
util::LayoutCreator<LogsPage> layoutCreator(this);
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
{
auto form = layout.emplace<QFormLayout>();
// clang-format off
form->addRow("Enabled:", this->createCheckBox("Enable logging", settings.enableLogging));
// clang-format on
}
layout->addStretch(1);
}
} // namespace settingspages
} // namespace widgets
} // namespace chatterino