Simple logging implemented

Message now stores a copy of the content string
Use .nick() instead of .account() to get the users lowercase name (login name)
This commit is contained in:
Rasmus Karlsson 2017-03-11 11:32:19 +01:00
parent 5dbe6990bd
commit 46f432890f
18 changed files with 338 additions and 66 deletions

View file

@ -1,5 +1,6 @@
#include "channel.h" #include "channel.h"
#include "emotes.h" #include "emotes.h"
#include "logging/loggingmanager.h"
#include "messages/message.h" #include "messages/message.h"
#include "windows.h" #include "windows.h"
@ -24,6 +25,7 @@ Channel::Channel(const QString &channel)
"/subscribe?ref=in_chat_subscriber_link") "/subscribe?ref=in_chat_subscriber_link")
, channelLink("https://twitch.tv/" + name) , channelLink("https://twitch.tv/" + name)
, popoutPlayerLink("https://player.twitch.tv/?channel=" + name) , popoutPlayerLink("https://player.twitch.tv/?channel=" + name)
, loggingChannel(logging::get(name))
{ {
reloadChannelEmotes(); reloadChannelEmotes();
} }
@ -127,6 +129,10 @@ Channel::addMessage(std::shared_ptr<messages::Message> message)
{ {
std::shared_ptr<messages::Message> deleted; std::shared_ptr<messages::Message> deleted;
if (this->loggingChannel.get() != nullptr) {
this->loggingChannel->append(message);
}
if (this->messages.appendItem(message, deleted)) { if (this->messages.appendItem(message, deleted)) {
this->messageRemovedFromStart(deleted); this->messageRemovedFromStart(deleted);
} }
@ -135,4 +141,5 @@ Channel::addMessage(std::shared_ptr<messages::Message> message)
Windows::repaintVisibleChatWidgets(this); Windows::repaintVisibleChatWidgets(this);
} }
}
} // namespace chatterino

View file

@ -2,6 +2,7 @@
#define CHANNEL_H #define CHANNEL_H
#include "concurrentmap.h" #include "concurrentmap.h"
#include "logging/loggingchannel.h"
#include "messages/lazyloadedimage.h" #include "messages/lazyloadedimage.h"
#include "messages/limitedqueue.h" #include "messages/limitedqueue.h"
@ -128,10 +129,12 @@ private:
int streamViewerCount; int streamViewerCount;
QString streamStatus; QString streamStatus;
QString streamGame; QString streamGame;
std::shared_ptr<logging::Channel> loggingChannel;
void reloadBttvEmotes(); void reloadBttvEmotes();
void reloadFfzEmotes(); void reloadFfzEmotes();
}; };
}
} // namespace chatterino
#endif // CHANNEL_H #endif // CHANNEL_H

View file

@ -3,13 +3,39 @@
namespace chatterino { namespace chatterino {
std::shared_ptr<Channel> Channels::whispers(new Channel(QString("/whispers"))); static std::shared_ptr<Channel> whispers(nullptr);
std::shared_ptr<Channel> Channels::mentions(new Channel(QString("/mentions"))); static std::shared_ptr<Channel> mentions(nullptr);
std::shared_ptr<Channel> Channels::empty(new Channel(QString(""))); static std::shared_ptr<Channel> empty(nullptr);
QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> Channels::channels; QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> Channels::channels;
QMutex Channels::channelsMutex; QMutex Channels::channelsMutex;
void
initChannels()
{
whispers.reset(new Channel(QString("/whispers")));
mentions.reset(new Channel(QString("/mentions")));
empty.reset(new Channel(QString("")));
}
std::shared_ptr<Channel>
Channels::getWhispers()
{
return whispers;
}
std::shared_ptr<Channel>
Channels::getMentions()
{
return mentions;
}
std::shared_ptr<Channel>
Channels::getEmpty()
{
return empty;
}
const std::vector<std::shared_ptr<Channel>> const std::vector<std::shared_ptr<Channel>>
Channels::getItems() Channels::getItems()
{ {
@ -60,20 +86,20 @@ Channels::getChannel(const QString &channel)
if (channel.length() > 1 && channel.at(0) == '/') { if (channel.length() > 1 && channel.at(0) == '/') {
if (c == "/whispers") { if (c == "/whispers") {
return Channels::whispers; return whispers;
} }
if (c == "/mentions") { if (c == "/mentions") {
return Channels::mentions; return mentions;
} }
return Channels::empty; return empty;
} }
auto a = Channels::channels.find(c); auto a = Channels::channels.find(c);
if (a == Channels::channels.end()) { if (a == Channels::channels.end()) {
return Channels::empty; return empty;
} }
return std::get<0>(a.value()); return std::get<0>(a.value());
@ -103,4 +129,5 @@ Channels::removeChannel(const QString &channel)
Channels::channels.remove(c); Channels::channels.remove(c);
} }
} }
}
} // namespace chatterino

View file

@ -5,26 +5,14 @@
namespace chatterino { namespace chatterino {
void initChannels();
class Channels class Channels
{ {
public: public:
static std::shared_ptr<Channel> static std::shared_ptr<Channel> getWhispers();
getWhispers() static std::shared_ptr<Channel> getMentions();
{ static std::shared_ptr<Channel> getEmpty();
return whispers;
}
static std::shared_ptr<Channel>
getMentions()
{
return mentions;
}
static std::shared_ptr<Channel>
getEmpty()
{
return empty;
}
const static std::vector<std::shared_ptr<Channel>> getItems(); const static std::vector<std::shared_ptr<Channel>> getItems();
@ -37,12 +25,10 @@ private:
{ {
} }
static std::shared_ptr<Channel> whispers;
static std::shared_ptr<Channel> mentions;
static std::shared_ptr<Channel> empty;
static QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> channels; static QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> channels;
static QMutex channelsMutex; static QMutex channelsMutex;
}; };
}
} // namespace chatterino
#endif // CHANNELS_H #endif // CHANNELS_H

View file

@ -53,8 +53,9 @@ SOURCES += main.cpp\
widgets/settingsdialogtab.cpp \ widgets/settingsdialogtab.cpp \
widgets/textinputdialog.cpp \ widgets/textinputdialog.cpp \
windows.cpp \ windows.cpp \
logging.cpp \ messages/messageref.cpp \
messages/messageref.cpp logging/loggingmanager.cpp \
logging/loggingchannel.cpp
HEADERS += account.h \ HEADERS += account.h \
asyncexec.h \ asyncexec.h \
@ -96,10 +97,11 @@ HEADERS += account.h \
windows.h \ windows.h \
widgets/resizingtextedit.h \ widgets/resizingtextedit.h \
settingssnapshot.h \ settingssnapshot.h \
logging.h \
messages/limitedqueue.h \ messages/limitedqueue.h \
messages/limitedqueuesnapshot.h \ messages/limitedqueuesnapshot.h \
messages/messageref.h messages/messageref.h \
logging/loggingmanager.h \
logging/loggingchannel.h
PRECOMPILED_HEADER = PRECOMPILED_HEADER =

View file

@ -12,6 +12,8 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QString> #include <QString>
#include <memory>
namespace chatterino { namespace chatterino {
class IrcManager class IrcManager

View file

@ -1,6 +0,0 @@
#include "logging.h"
Logging::Logging()
{
}

View file

@ -1,11 +0,0 @@
#ifndef LOGGING_H
#define LOGGING_H
class Logging
{
public:
private:
Logging();
};
#endif // LOGGING_H

View file

@ -0,0 +1,82 @@
#include "loggingchannel.h"
#include "loggingmanager.h"
#include <QDir>
#include <ctime>
namespace chatterino {
namespace logging {
Channel::Channel(const QString &_channelName, const QString &_baseDirectory)
: channelName(_channelName)
, baseDirectory(_baseDirectory)
{
QDateTime now = QDateTime::currentDateTime();
this->fileName =
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.open(QIODevice::Append);
this->appendLine(this->generateOpeningString(now));
}
Channel::~Channel()
{
this->appendLine(this->generateClosingString());
this->fileHandle.close();
}
void
Channel::append(std::shared_ptr<messages::Message> message)
{
QDateTime now = QDateTime::currentDateTime();
QString str;
str.append('[');
str.append(now.toString("HH:mm:ss"));
str.append("] ");
str.append(message->getUserName());
str.append(": ");
str.append(message->getContent());
str.append('\n');
this->appendLine(str);
}
QString
Channel::generateOpeningString(const QDateTime &now) const
{
QString ret = QLatin1Literal("# Start logging at ");
ret.append(now.toString("yyyy-MM-dd HH:mm:ss "));
ret.append(now.timeZoneAbbreviation());
ret.append('\n');
return ret;
}
QString
Channel::generateClosingString(const QDateTime &now) const
{
QString ret = QLatin1Literal("# Stop logging at ");
ret.append(now.toString("yyyy-MM-dd HH:mm:ss"));
ret.append(now.timeZoneAbbreviation());
ret.append('\n');
return ret;
}
void
Channel::appendLine(const QString &line)
{
this->fileHandle.write(line.toUtf8());
this->fileHandle.flush();
}
} // namespace logging
} // namespace chatterino

42
logging/loggingchannel.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef LOGGINGCHANNEL_H
#define LOGGINGCHANNEL_H
#include "messages/message.h"
#include <QDateTime>
#include <QFile>
#include <QString>
#include <memory>
namespace chatterino {
namespace logging {
class Channel
{
public:
explicit Channel(const QString &_channelName,
const QString &_baseDirectory);
~Channel();
void append(std::shared_ptr<messages::Message> message);
private:
QString generateOpeningString(
const QDateTime &now = QDateTime::currentDateTime()) const;
QString generateClosingString(
const QDateTime &now = QDateTime::currentDateTime()) const;
void appendLine(const QString &line);
private:
QString channelName;
const QString &baseDirectory;
QString fileName;
QFile fileHandle;
};
} // namespace logging
} // namespace chatterino
#endif // LOGGINGCHANNEL_H

104
logging/loggingmanager.cpp Normal file
View file

@ -0,0 +1,104 @@
#include "loggingmanager.h"
#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

17
logging/loggingmanager.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef LOGGINGMANAGER_H
#define LOGGINGMANAGER_H
#include "loggingchannel.h"
#include <memory>
namespace chatterino {
namespace logging {
void init();
std::shared_ptr<Channel> get(const QString &channelName);
} // namespace logging
} // namespace chatterino
#endif // LOGGINGMANAGER_H

View file

@ -3,6 +3,7 @@
#include "emojis.h" #include "emojis.h"
#include "emotes.h" #include "emotes.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "logging/loggingmanager.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
#include "widgets/mainwindow.h" #include "widgets/mainwindow.h"
@ -20,6 +21,8 @@ main(int argc, char *argv[])
{ {
QApplication a(argc, argv); QApplication a(argc, argv);
chatterino::logging::init();
chatterino::initChannels();
Settings::getInstance().load(); Settings::getInstance().load();
Resources::load(); Resources::load();
Emojis::loadEmojis(); Emojis::loadEmojis();

View file

@ -1,4 +1,5 @@
#include "messages/message.h" #include "messages/message.h"
#include "channel.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "emojis.h" #include "emojis.h"
#include "emotes.h" #include "emotes.h"
@ -30,6 +31,7 @@ Message::Message(const QString &text)
Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel,
bool enablePingSound, bool isReceivedWhisper, bool enablePingSound, bool isReceivedWhisper,
bool isSentWhisper, bool includeChannel) bool isSentWhisper, bool includeChannel)
: content(ircMessage.content())
{ {
this->parseTime = std::chrono::system_clock::now(); this->parseTime = std::chrono::system_clock::now();
@ -144,14 +146,10 @@ Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel,
} }
// username // username
this->userName = ircMessage.account(); this->userName = ircMessage.nick();
if (this->userName.isEmpty()) { if (this->userName.isEmpty()) {
auto iterator = tags.find("login"); this->userName = tags.value(QLatin1String("login")).toString();
if (iterator != tags.end()) {
this->userName = iterator.value().toString();
}
} }
QString displayName; QString displayName;

View file

@ -1,15 +1,18 @@
#ifndef MESSAGE_H #ifndef MESSAGE_H
#define MESSAGE_H #define MESSAGE_H
#include "channel.h"
#include "messages/word.h" #include "messages/word.h"
#include "messages/wordpart.h" #include "messages/wordpart.h"
#include <IrcMessage> #include <IrcMessage>
#include <QVector> #include <QVector>
#include <chrono> #include <chrono>
namespace chatterino { namespace chatterino {
class Channel;
namespace messages { namespace messages {
class Message class Message
@ -54,6 +57,18 @@ public:
return displayName; return displayName;
} }
inline const QString &
getContent() const
{
return this->content;
}
inline const std::chrono::time_point<std::chrono::system_clock> &
getParseTime() const
{
return this->parseTime;
}
std::vector<Word> & std::vector<Word> &
getWords() getWords()
{ {
@ -91,6 +106,7 @@ private:
QString userName = ""; QString userName = "";
QString displayName = ""; QString displayName = "";
QString content;
QString id = ""; QString id = "";
std::vector<Word> words; std::vector<Word> words;

View file

@ -33,5 +33,6 @@ Word::Word(const QString &text, Type type, const QColor &color,
, characterWidthCache() , characterWidthCache()
{ {
} }
}
} } // namespace messages
} // namespace chatterino

View file

@ -208,7 +208,8 @@ private:
std::vector<short> characterWidthCache; std::vector<short> characterWidthCache;
}; };
}
} } // namespace messages
} // namespace chatterino
#endif // WORD_H #endif // WORD_H

View file

@ -76,8 +76,6 @@ ChatWidget::setChannelName(const QString &name)
auto messageRef = new messages::MessageRef(message); auto messageRef = new messages::MessageRef(message);
qDebug() << "xD";
this->messages.appendItem( this->messages.appendItem(
std::shared_ptr<messages::MessageRef>(messageRef), deleted); std::shared_ptr<messages::MessageRef>(messageRef), deleted);
}); });